1 /*
2 MIT License
3
4 Copyright (c) 2018-2020 Jonathan Young
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24 /*
25 thekla_atlas
26 https://github.com/Thekla/thekla_atlas
27 MIT License
28 Copyright (c) 2013 Thekla, Inc
29 Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com>
30
31 Fast-BVH
32 https://github.com/brandonpelfrey/Fast-BVH
33 MIT License
34 Copyright (c) 2012 Brandon Pelfrey
35 */
36 #include <atomic>
37 #include <condition_variable>
38 #include <mutex>
39 #include <thread>
40 #include <assert.h>
41 #include <float.h> // FLT_MAX
42 #include <limits.h>
43 #include <math.h>
44 #define __STDC_LIMIT_MACROS
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include "xatlas.h"
49
50 #ifndef XA_DEBUG
51 #ifdef NDEBUG
52 #define XA_DEBUG 0
53 #else
54 #define XA_DEBUG 1
55 #endif
56 #endif
57
58 #ifndef XA_PROFILE
59 #define XA_PROFILE 0
60 #endif
61 #if XA_PROFILE
62 #include <time.h>
63 #endif
64
65 #ifndef XA_MULTITHREADED
66 #define XA_MULTITHREADED 1
67 #endif
68
69 #define XA_STR(x) #x
70 #define XA_XSTR(x) XA_STR(x)
71
72 #ifndef XA_ASSERT
73 #define XA_ASSERT(exp) if (!(exp)) { XA_PRINT_WARNING("\rASSERT: %s %s %d\n", XA_XSTR(exp), __FILE__, __LINE__); }
74 #endif
75
76 #ifndef XA_DEBUG_ASSERT
77 #define XA_DEBUG_ASSERT(exp) assert(exp)
78 #endif
79
80 #ifndef XA_PRINT
81 #define XA_PRINT(...) \
82 if (xatlas::internal::s_print && xatlas::internal::s_printVerbose) \
83 xatlas::internal::s_print(__VA_ARGS__);
84 #endif
85
86 #ifndef XA_PRINT_WARNING
87 #define XA_PRINT_WARNING(...) \
88 if (xatlas::internal::s_print) \
89 xatlas::internal::s_print(__VA_ARGS__);
90 #endif
91
92 #define XA_ALLOC(tag, type) (type *)internal::Realloc(nullptr, sizeof(type), tag, __FILE__, __LINE__)
93 #define XA_ALLOC_ARRAY(tag, type, num) (type *)internal::Realloc(nullptr, sizeof(type) * (num), tag, __FILE__, __LINE__)
94 #define XA_REALLOC(tag, ptr, type, num) (type *)internal::Realloc(ptr, sizeof(type) * (num), tag, __FILE__, __LINE__)
95 #define XA_REALLOC_SIZE(tag, ptr, size) (uint8_t *)internal::Realloc(ptr, size, tag, __FILE__, __LINE__)
96 #define XA_FREE(ptr) internal::Realloc(ptr, 0, internal::MemTag::Default, __FILE__, __LINE__)
97 #define XA_NEW(tag, type) new (XA_ALLOC(tag, type)) type()
98 #define XA_NEW_ARGS(tag, type, ...) new (XA_ALLOC(tag, type)) type(__VA_ARGS__)
99
100 #ifdef _MSC_VER
101 #define XA_INLINE __forceinline
102 #else
103 #define XA_INLINE inline
104 #endif
105
106 #if defined(__clang__) || defined(__GNUC__)
107 #define XA_NODISCARD [[nodiscard]]
108 #elif defined(_MSC_VER)
109 #define XA_NODISCARD _Check_return_
110 #else
111 #define XA_NODISCARD
112 #endif
113
114 #define XA_UNUSED(a) ((void)(a))
115
116 #define XA_MERGE_CHARTS 1
117 #define XA_MERGE_CHARTS_MIN_NORMAL_DEVIATION 0.5f
118 #define XA_RECOMPUTE_CHARTS 1
119 #define XA_CLOSE_HOLES_CHECK_EDGE_INTERSECTION 0
120 #define XA_FIX_INTERNAL_BOUNDARY_LOOPS 1
121 #define XA_PRINT_CHART_WARNINGS 0
122
123 #define XA_DEBUG_HEAP 0
124 #define XA_DEBUG_SINGLE_CHART 0
125 #define XA_DEBUG_ALL_CHARTS_INVALID 0
126 #define XA_DEBUG_EXPORT_ATLAS_IMAGES 0
127 #define XA_DEBUG_EXPORT_ATLAS_IMAGES_PER_CHART 0 // Export an atlas image after each chart is added.
128 #define XA_DEBUG_EXPORT_BOUNDARY_GRID 0
129 #define XA_DEBUG_EXPORT_TGA (XA_DEBUG_EXPORT_ATLAS_IMAGES || XA_DEBUG_EXPORT_BOUNDARY_GRID)
130 #define XA_DEBUG_EXPORT_OBJ_FACE_GROUPS 0
131 #define XA_DEBUG_EXPORT_OBJ_CHART_GROUPS 0
132 #define XA_DEBUG_EXPORT_OBJ_PLANAR_REGIONS 0
133 #define XA_DEBUG_EXPORT_OBJ_CHARTS 0
134 #define XA_DEBUG_EXPORT_OBJ_BEFORE_FIX_TJUNCTION 0
135 #define XA_DEBUG_EXPORT_OBJ_CLOSE_HOLES_ERROR 0
136 #define XA_DEBUG_EXPORT_OBJ_CHARTS_AFTER_PARAMETERIZATION 0
137 #define XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION 0
138 #define XA_DEBUG_EXPORT_OBJ_RECOMPUTED_CHARTS 0
139
140 #define XA_DEBUG_EXPORT_OBJ (0 \
141 || XA_DEBUG_EXPORT_OBJ_FACE_GROUPS \
142 || XA_DEBUG_EXPORT_OBJ_CHART_GROUPS \
143 || XA_DEBUG_EXPORT_OBJ_PLANAR_REGIONS \
144 || XA_DEBUG_EXPORT_OBJ_CHARTS \
145 || XA_DEBUG_EXPORT_OBJ_BEFORE_FIX_TJUNCTION \
146 || XA_DEBUG_EXPORT_OBJ_CLOSE_HOLES_ERROR \
147 || XA_DEBUG_EXPORT_OBJ_CHARTS_AFTER_PARAMETERIZATION \
148 || XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION \
149 || XA_DEBUG_EXPORT_OBJ_RECOMPUTED_CHARTS)
150
151 #ifdef _MSC_VER
152 #define XA_FOPEN(_file, _filename, _mode) { if (fopen_s(&_file, _filename, _mode) != 0) _file = NULL; }
153 #define XA_SPRINTF(_buffer, _size, _format, ...) sprintf_s(_buffer, _size, _format, __VA_ARGS__)
154 #else
155 #define XA_FOPEN(_file, _filename, _mode) _file = fopen(_filename, _mode)
156 #define XA_SPRINTF(_buffer, _size, _format, ...) sprintf(_buffer, _format, __VA_ARGS__)
157 #endif
158
159 namespace xatlas {
160 namespace internal {
161
162 static ReallocFunc s_realloc = realloc;
163 static FreeFunc s_free = free;
164 static PrintFunc s_print = printf;
165 static bool s_printVerbose = false;
166
167 #if XA_PROFILE
168 #define XA_PROFILE_START(var) const clock_t var##Start = clock();
169 #define XA_PROFILE_END(var) internal::s_profile.var += clock() - var##Start;
170 #define XA_PROFILE_PRINT_AND_RESET(label, var) XA_PRINT("%s%.2f seconds (%g ms)\n", label, internal::clockToSeconds(internal::s_profile.var), internal::clockToMs(internal::s_profile.var)); internal::s_profile.var = 0;
171 #define XA_PROFILE_ALLOC 0
172
173 struct ProfileData
174 {
175 #if XA_PROFILE_ALLOC
176 std::atomic<clock_t> alloc;
177 #endif
178 clock_t addMeshReal;
179 clock_t addMeshCopyData;
180 std::atomic<clock_t> addMeshThread;
181 std::atomic<clock_t> addMeshCreateColocals;
182 clock_t computeChartsReal;
183 std::atomic<clock_t> computeChartsThread;
184 std::atomic<clock_t> createFaceGroups;
185 std::atomic<clock_t> extractInvalidMeshGeometry;
186 std::atomic<clock_t> chartGroupComputeChartsReal;
187 std::atomic<clock_t> chartGroupComputeChartsThread;
188 std::atomic<clock_t> createChartGroupMesh;
189 std::atomic<clock_t> createChartGroupMeshColocals;
190 std::atomic<clock_t> createChartGroupMeshBoundaries;
191 std::atomic<clock_t> buildAtlas;
192 std::atomic<clock_t> buildAtlasInit;
193 std::atomic<clock_t> planarCharts;
194 std::atomic<clock_t> clusteredCharts;
195 std::atomic<clock_t> clusteredChartsPlaceSeeds;
196 std::atomic<clock_t> clusteredChartsPlaceSeedsBoundaryIntersection;
197 std::atomic<clock_t> clusteredChartsRelocateSeeds;
198 std::atomic<clock_t> clusteredChartsReset;
199 std::atomic<clock_t> clusteredChartsGrow;
200 std::atomic<clock_t> clusteredChartsGrowBoundaryIntersection;
201 std::atomic<clock_t> clusteredChartsMerge;
202 std::atomic<clock_t> clusteredChartsFillHoles;
203 std::atomic<clock_t> copyChartFaces;
204 clock_t parameterizeChartsReal;
205 std::atomic<clock_t> parameterizeChartsThread;
206 std::atomic<clock_t> createChartMesh;
207 std::atomic<clock_t> fixChartMeshTJunctions;
208 std::atomic<clock_t> closeChartMeshHoles;
209 std::atomic<clock_t> parameterizeChartsOrthogonal;
210 std::atomic<clock_t> parameterizeChartsLSCM;
211 std::atomic<clock_t> parameterizeChartsRecompute;
212 std::atomic<clock_t> parameterizeChartsPiecewise;
213 std::atomic<clock_t> parameterizeChartsPiecewiseBoundaryIntersection;
214 std::atomic<clock_t> parameterizeChartsEvaluateQuality;
215 clock_t packCharts;
216 clock_t packChartsAddCharts;
217 std::atomic<clock_t> packChartsAddChartsThread;
218 std::atomic<clock_t> packChartsAddChartsRestoreTexcoords;
219 clock_t packChartsRasterize;
220 clock_t packChartsDilate;
221 clock_t packChartsFindLocation;
222 clock_t packChartsBlit;
223 clock_t buildOutputMeshes;
224 };
225
226 static ProfileData s_profile;
227
clockToMs(clock_t c)228 static double clockToMs(clock_t c)
229 {
230 return c * 1000.0 / CLOCKS_PER_SEC;
231 }
232
clockToSeconds(clock_t c)233 static double clockToSeconds(clock_t c)
234 {
235 return c / (double)CLOCKS_PER_SEC;
236 }
237 #else
238 #define XA_PROFILE_START(var)
239 #define XA_PROFILE_END(var)
240 #define XA_PROFILE_PRINT_AND_RESET(label, var)
241 #define XA_PROFILE_ALLOC 0
242 #endif
243
244 struct MemTag
245 {
246 enum
247 {
248 Default,
249 BitImage,
250 BVH,
251 Matrix,
252 Mesh,
253 MeshBoundaries,
254 MeshColocals,
255 MeshEdgeMap,
256 MeshIndices,
257 MeshNormals,
258 MeshPositions,
259 MeshTexcoords,
260 OpenNL,
261 SegmentAtlasChartCandidates,
262 SegmentAtlasChartFaces,
263 SegmentAtlasMeshData,
264 SegmentAtlasPlanarRegions,
265 Count
266 };
267 };
268
269 #if XA_DEBUG_HEAP
270 struct AllocHeader
271 {
272 size_t size;
273 const char *file;
274 int line;
275 int tag;
276 uint32_t id;
277 AllocHeader *prev, *next;
278 bool free;
279 };
280
281 static std::mutex s_allocMutex;
282 static AllocHeader *s_allocRoot = nullptr;
283 static size_t s_allocTotalCount = 0, s_allocTotalSize = 0, s_allocPeakSize = 0, s_allocCount[MemTag::Count] = { 0 }, s_allocTotalTagSize[MemTag::Count] = { 0 }, s_allocPeakTagSize[MemTag::Count] = { 0 };
284 static uint32_t s_allocId =0 ;
285 static constexpr uint32_t kAllocRedzone = 0x12345678;
286
Realloc(void * ptr,size_t size,int tag,const char * file,int line)287 static void *Realloc(void *ptr, size_t size, int tag, const char *file, int line)
288 {
289 std::unique_lock<std::mutex> lock(s_allocMutex);
290 if (!size && !ptr)
291 return nullptr;
292 uint8_t *realPtr = nullptr;
293 AllocHeader *header = nullptr;
294 if (ptr) {
295 realPtr = ((uint8_t *)ptr) - sizeof(AllocHeader);
296 header = (AllocHeader *)realPtr;
297 }
298 if (realPtr && size) {
299 s_allocTotalSize -= header->size;
300 s_allocTotalTagSize[header->tag] -= header->size;
301 // realloc, remove.
302 if (header->prev)
303 header->prev->next = header->next;
304 else
305 s_allocRoot = header->next;
306 if (header->next)
307 header->next->prev = header->prev;
308 }
309 if (!size) {
310 s_allocTotalSize -= header->size;
311 s_allocTotalTagSize[header->tag] -= header->size;
312 XA_ASSERT(!header->free); // double free
313 header->free = true;
314 return nullptr;
315 }
316 size += sizeof(AllocHeader) + sizeof(kAllocRedzone);
317 uint8_t *newPtr = (uint8_t *)s_realloc(realPtr, size);
318 if (!newPtr)
319 return nullptr;
320 header = (AllocHeader *)newPtr;
321 header->size = size;
322 header->file = file;
323 header->line = line;
324 header->tag = tag;
325 header->id = s_allocId++;
326 header->free = false;
327 if (!s_allocRoot) {
328 s_allocRoot = header;
329 header->prev = header->next = 0;
330 } else {
331 header->prev = nullptr;
332 header->next = s_allocRoot;
333 s_allocRoot = header;
334 header->next->prev = header;
335 }
336 s_allocTotalCount++;
337 s_allocTotalSize += size;
338 if (s_allocTotalSize > s_allocPeakSize)
339 s_allocPeakSize = s_allocTotalSize;
340 s_allocCount[tag]++;
341 s_allocTotalTagSize[tag] += size;
342 if (s_allocTotalTagSize[tag] > s_allocPeakTagSize[tag])
343 s_allocPeakTagSize[tag] = s_allocTotalTagSize[tag];
344 auto redzone = (uint32_t *)(newPtr + size - sizeof(kAllocRedzone));
345 *redzone = kAllocRedzone;
346 return newPtr + sizeof(AllocHeader);
347 }
348
ReportLeaks()349 static void ReportLeaks()
350 {
351 printf("Checking for memory leaks...\n");
352 bool anyLeaks = false;
353 AllocHeader *header = s_allocRoot;
354 while (header) {
355 if (!header->free) {
356 printf(" Leak: ID %u, %zu bytes, %s %d\n", header->id, header->size, header->file, header->line);
357 anyLeaks = true;
358 }
359 auto redzone = (const uint32_t *)((const uint8_t *)header + header->size - sizeof(kAllocRedzone));
360 if (*redzone != kAllocRedzone)
361 printf(" Redzone corrupted: %zu bytes %s %d\n", header->size, header->file, header->line);
362 header = header->next;
363 }
364 if (!anyLeaks)
365 printf(" No memory leaks\n");
366 header = s_allocRoot;
367 while (header) {
368 AllocHeader *destroy = header;
369 header = header->next;
370 s_realloc(destroy, 0);
371 }
372 s_allocRoot = nullptr;
373 s_allocTotalSize = s_allocPeakSize = 0;
374 for (int i = 0; i < MemTag::Count; i++)
375 s_allocTotalTagSize[i] = s_allocPeakTagSize[i] = 0;
376 }
377
PrintMemoryUsage()378 static void PrintMemoryUsage()
379 {
380 XA_PRINT("Total allocations: %zu\n", s_allocTotalCount);
381 XA_PRINT("Memory usage: %0.2fMB current, %0.2fMB peak\n", internal::s_allocTotalSize / 1024.0f / 1024.0f, internal::s_allocPeakSize / 1024.0f / 1024.0f);
382 static const char *labels[] = { // Sync with MemTag
383 "Default",
384 "BitImage",
385 "BVH",
386 "Matrix",
387 "Mesh",
388 "MeshBoundaries",
389 "MeshColocals",
390 "MeshEdgeMap",
391 "MeshIndices",
392 "MeshNormals",
393 "MeshPositions",
394 "MeshTexcoords",
395 "OpenNL",
396 "SegmentAtlasChartCandidates",
397 "SegmentAtlasChartFaces",
398 "SegmentAtlasMeshData",
399 "SegmentAtlasPlanarRegions"
400 };
401 for (int i = 0; i < MemTag::Count; i++) {
402 XA_PRINT(" %s: %zu allocations, %0.2fMB current, %0.2fMB peak\n", labels[i], internal::s_allocCount[i], internal::s_allocTotalTagSize[i] / 1024.0f / 1024.0f, internal::s_allocPeakTagSize[i] / 1024.0f / 1024.0f);
403 }
404 }
405
406 #define XA_PRINT_MEM_USAGE internal::PrintMemoryUsage();
407 #else
Realloc(void * ptr,size_t size,int,const char *,int)408 static void *Realloc(void *ptr, size_t size, int /*tag*/, const char * /*file*/, int /*line*/)
409 {
410 if (size == 0 && !ptr)
411 return nullptr;
412 if (size == 0 && s_free) {
413 s_free(ptr);
414 return nullptr;
415 }
416 #if XA_PROFILE_ALLOC
417 XA_PROFILE_START(alloc)
418 #endif
419 void *mem = s_realloc(ptr, size);
420 #if XA_PROFILE_ALLOC
421 XA_PROFILE_END(alloc)
422 #endif
423 XA_DEBUG_ASSERT(size <= 0 || (size > 0 && mem));
424 return mem;
425 }
426 #define XA_PRINT_MEM_USAGE
427 #endif
428
429 static constexpr float kPi = 3.14159265358979323846f;
430 static constexpr float kPi2 = 6.28318530717958647692f;
431 static constexpr float kEpsilon = 0.0001f;
432 static constexpr float kAreaEpsilon = FLT_EPSILON;
433 static constexpr float kNormalEpsilon = 0.001f;
434
align(int x,int a)435 static int align(int x, int a)
436 {
437 return (x + a - 1) & ~(a - 1);
438 }
439
440 template <typename T>
max(const T & a,const T & b)441 static T max(const T &a, const T &b)
442 {
443 return a > b ? a : b;
444 }
445
446 template <typename T>
min(const T & a,const T & b)447 static T min(const T &a, const T &b)
448 {
449 return a < b ? a : b;
450 }
451
452 template <typename T>
max3(const T & a,const T & b,const T & c)453 static T max3(const T &a, const T &b, const T &c)
454 {
455 return max(a, max(b, c));
456 }
457
458 /// Return the maximum of the three arguments.
459 template <typename T>
min3(const T & a,const T & b,const T & c)460 static T min3(const T &a, const T &b, const T &c)
461 {
462 return min(a, min(b, c));
463 }
464
465 /// Clamp between two values.
466 template <typename T>
clamp(const T & x,const T & a,const T & b)467 static T clamp(const T &x, const T &a, const T &b)
468 {
469 return min(max(x, a), b);
470 }
471
472 template <typename T>
swap(T & a,T & b)473 static void swap(T &a, T &b)
474 {
475 T temp = a;
476 a = b;
477 b = temp;
478 }
479
480 union FloatUint32
481 {
482 float f;
483 uint32_t u;
484 };
485
isFinite(float f)486 static bool isFinite(float f)
487 {
488 FloatUint32 fu;
489 fu.f = f;
490 return fu.u != 0x7F800000u && fu.u != 0x7F800001u;
491 }
492
isNan(float f)493 static bool isNan(float f)
494 {
495 return f != f;
496 }
497
498 // Robust floating point comparisons:
499 // http://realtimecollisiondetection.net/blog/?p=89
equal(const float f0,const float f1,const float epsilon)500 static bool equal(const float f0, const float f1, const float epsilon)
501 {
502 //return fabs(f0-f1) <= epsilon;
503 return fabs(f0 - f1) <= epsilon * max3(1.0f, fabsf(f0), fabsf(f1));
504 }
505
ftoi_ceil(float val)506 static int ftoi_ceil(float val)
507 {
508 return (int)ceilf(val);
509 }
510
isZero(const float f,const float epsilon)511 static bool isZero(const float f, const float epsilon)
512 {
513 return fabs(f) <= epsilon;
514 }
515
square(float f)516 static float square(float f)
517 {
518 return f * f;
519 }
520
521 /** Return the next power of two.
522 * @see http://graphics.stanford.edu/~seander/bithacks.html
523 * @warning Behaviour for 0 is undefined.
524 * @note isPowerOfTwo(x) == true -> nextPowerOfTwo(x) == x
525 * @note nextPowerOfTwo(x) = 2 << log2(x-1)
526 */
nextPowerOfTwo(uint32_t x)527 static uint32_t nextPowerOfTwo(uint32_t x)
528 {
529 XA_DEBUG_ASSERT( x != 0 );
530 // On modern CPUs this is supposed to be as fast as using the bsr instruction.
531 x--;
532 x |= x >> 1;
533 x |= x >> 2;
534 x |= x >> 4;
535 x |= x >> 8;
536 x |= x >> 16;
537 return x + 1;
538 }
539
540 class Vector2
541 {
542 public:
Vector2()543 Vector2() {}
Vector2(float f)544 explicit Vector2(float f) : x(f), y(f) {}
Vector2(float x,float y)545 Vector2(float x, float y): x(x), y(y) {}
546
operator -() const547 Vector2 operator-() const
548 {
549 return Vector2(-x, -y);
550 }
551
operator +=(const Vector2 & v)552 void operator+=(const Vector2 &v)
553 {
554 x += v.x;
555 y += v.y;
556 }
557
operator -=(const Vector2 & v)558 void operator-=(const Vector2 &v)
559 {
560 x -= v.x;
561 y -= v.y;
562 }
563
operator *=(float s)564 void operator*=(float s)
565 {
566 x *= s;
567 y *= s;
568 }
569
operator *=(const Vector2 & v)570 void operator*=(const Vector2 &v)
571 {
572 x *= v.x;
573 y *= v.y;
574 }
575
576 float x, y;
577 };
578
operator ==(const Vector2 & a,const Vector2 & b)579 static bool operator==(const Vector2 &a, const Vector2 &b)
580 {
581 return a.x == b.x && a.y == b.y;
582 }
583
operator !=(const Vector2 & a,const Vector2 & b)584 static bool operator!=(const Vector2 &a, const Vector2 &b)
585 {
586 return a.x != b.x || a.y != b.y;
587 }
588
589 /*static Vector2 operator+(const Vector2 &a, const Vector2 &b)
590 {
591 return Vector2(a.x + b.x, a.y + b.y);
592 }*/
593
operator -(const Vector2 & a,const Vector2 & b)594 static Vector2 operator-(const Vector2 &a, const Vector2 &b)
595 {
596 return Vector2(a.x - b.x, a.y - b.y);
597 }
598
operator *(const Vector2 & v,float s)599 static Vector2 operator*(const Vector2 &v, float s)
600 {
601 return Vector2(v.x * s, v.y * s);
602 }
603
dot(const Vector2 & a,const Vector2 & b)604 static float dot(const Vector2 &a, const Vector2 &b)
605 {
606 return a.x * b.x + a.y * b.y;
607 }
608
lengthSquared(const Vector2 & v)609 static float lengthSquared(const Vector2 &v)
610 {
611 return v.x * v.x + v.y * v.y;
612 }
613
length(const Vector2 & v)614 static float length(const Vector2 &v)
615 {
616 return sqrtf(lengthSquared(v));
617 }
618
619 #if XA_DEBUG
isNormalized(const Vector2 & v,float epsilon=kNormalEpsilon)620 static bool isNormalized(const Vector2 &v, float epsilon = kNormalEpsilon)
621 {
622 return equal(length(v), 1, epsilon);
623 }
624 #endif
625
normalize(const Vector2 & v,float epsilon)626 static Vector2 normalize(const Vector2 &v, float epsilon)
627 {
628 float l = length(v);
629 XA_DEBUG_ASSERT(!isZero(l, epsilon));
630 XA_UNUSED(epsilon);
631 Vector2 n = v * (1.0f / l);
632 XA_DEBUG_ASSERT(isNormalized(n));
633 return n;
634 }
635
normalizeSafe(const Vector2 & v,const Vector2 & fallback,float epsilon)636 static Vector2 normalizeSafe(const Vector2 &v, const Vector2 &fallback, float epsilon)
637 {
638 float l = length(v);
639 if (isZero(l, epsilon))
640 return fallback;
641 return v * (1.0f / l);
642 }
643
equal(const Vector2 & v1,const Vector2 & v2,float epsilon)644 static bool equal(const Vector2 &v1, const Vector2 &v2, float epsilon)
645 {
646 return equal(v1.x, v2.x, epsilon) && equal(v1.y, v2.y, epsilon);
647 }
648
min(const Vector2 & a,const Vector2 & b)649 static Vector2 min(const Vector2 &a, const Vector2 &b)
650 {
651 return Vector2(min(a.x, b.x), min(a.y, b.y));
652 }
653
max(const Vector2 & a,const Vector2 & b)654 static Vector2 max(const Vector2 &a, const Vector2 &b)
655 {
656 return Vector2(max(a.x, b.x), max(a.y, b.y));
657 }
658
isFinite(const Vector2 & v)659 static bool isFinite(const Vector2 &v)
660 {
661 return isFinite(v.x) && isFinite(v.y);
662 }
663
triangleArea(const Vector2 & a,const Vector2 & b,const Vector2 & c)664 static float triangleArea(const Vector2 &a, const Vector2 &b, const Vector2 &c)
665 {
666 // IC: While it may be appealing to use the following expression:
667 //return (c.x * a.y + a.x * b.y + b.x * c.y - b.x * a.y - c.x * b.y - a.x * c.y) * 0.5f;
668 // That's actually a terrible idea. Small triangles far from the origin can end up producing fairly large floating point
669 // numbers and the results becomes very unstable and dependent on the order of the factors.
670 // Instead, it's preferable to subtract the vertices first, and multiply the resulting small values together. The result
671 // in this case is always much more accurate (as long as the triangle is small) and less dependent of the location of
672 // the triangle.
673 //return ((a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)) * 0.5f;
674 const Vector2 v0 = a - c;
675 const Vector2 v1 = b - c;
676 return (v0.x * v1.y - v0.y * v1.x) * 0.5f;
677 }
678
linesIntersect(const Vector2 & a1,const Vector2 & a2,const Vector2 & b1,const Vector2 & b2,float epsilon)679 static bool linesIntersect(const Vector2 &a1, const Vector2 &a2, const Vector2 &b1, const Vector2 &b2, float epsilon)
680 {
681 const Vector2 v0 = a2 - a1;
682 const Vector2 v1 = b2 - b1;
683 const float denom = -v1.x * v0.y + v0.x * v1.y;
684 if (equal(denom, 0.0f, epsilon))
685 return false;
686 const float s = (-v0.y * (a1.x - b1.x) + v0.x * (a1.y - b1.y)) / denom;
687 if (s > epsilon && s < 1.0f - epsilon) {
688 const float t = ( v1.x * (a1.y - b1.y) - v1.y * (a1.x - b1.x)) / denom;
689 return t > epsilon && t < 1.0f - epsilon;
690 }
691 return false;
692 }
693
694 struct Vector2i
695 {
Vector2ixatlas::internal::Vector2i696 Vector2i() {}
Vector2ixatlas::internal::Vector2i697 Vector2i(int32_t x, int32_t y) : x(x), y(y) {}
698
699 int32_t x, y;
700 };
701
702 class Vector3
703 {
704 public:
Vector3()705 Vector3() {}
Vector3(float f)706 explicit Vector3(float f) : x(f), y(f), z(f) {}
Vector3(float x,float y,float z)707 Vector3(float x, float y, float z) : x(x), y(y), z(z) {}
Vector3(const Vector2 & v,float z)708 Vector3(const Vector2 &v, float z) : x(v.x), y(v.y), z(z) {}
709
xy() const710 Vector2 xy() const
711 {
712 return Vector2(x, y);
713 }
714
operator -() const715 Vector3 operator-() const
716 {
717 return Vector3(-x, -y, -z);
718 }
719
operator +=(const Vector3 & v)720 void operator+=(const Vector3 &v)
721 {
722 x += v.x;
723 y += v.y;
724 z += v.z;
725 }
726
operator -=(const Vector3 & v)727 void operator-=(const Vector3 &v)
728 {
729 x -= v.x;
730 y -= v.y;
731 z -= v.z;
732 }
733
operator *=(float s)734 void operator*=(float s)
735 {
736 x *= s;
737 y *= s;
738 z *= s;
739 }
740
operator /=(float s)741 void operator/=(float s)
742 {
743 float is = 1.0f / s;
744 x *= is;
745 y *= is;
746 z *= is;
747 }
748
operator *=(const Vector3 & v)749 void operator*=(const Vector3 &v)
750 {
751 x *= v.x;
752 y *= v.y;
753 z *= v.z;
754 }
755
operator /=(const Vector3 & v)756 void operator/=(const Vector3 &v)
757 {
758 x /= v.x;
759 y /= v.y;
760 z /= v.z;
761 }
762
763 float x, y, z;
764 };
765
operator +(const Vector3 & a,const Vector3 & b)766 static Vector3 operator+(const Vector3 &a, const Vector3 &b)
767 {
768 return Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
769 }
770
operator -(const Vector3 & a,const Vector3 & b)771 static Vector3 operator-(const Vector3 &a, const Vector3 &b)
772 {
773 return Vector3(a.x - b.x, a.y - b.y, a.z - b.z);
774 }
775
cross(const Vector3 & a,const Vector3 & b)776 static Vector3 cross(const Vector3 &a, const Vector3 &b)
777 {
778 return Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
779 }
780
operator *(const Vector3 & v,float s)781 static Vector3 operator*(const Vector3 &v, float s)
782 {
783 return Vector3(v.x * s, v.y * s, v.z * s);
784 }
785
operator /(const Vector3 & v,float s)786 static Vector3 operator/(const Vector3 &v, float s)
787 {
788 return v * (1.0f / s);
789 }
790
dot(const Vector3 & a,const Vector3 & b)791 static float dot(const Vector3 &a, const Vector3 &b)
792 {
793 return a.x * b.x + a.y * b.y + a.z * b.z;
794 }
795
lengthSquared(const Vector3 & v)796 static float lengthSquared(const Vector3 &v)
797 {
798 return v.x * v.x + v.y * v.y + v.z * v.z;
799 }
800
length(const Vector3 & v)801 static float length(const Vector3 &v)
802 {
803 return sqrtf(lengthSquared(v));
804 }
805
isNormalized(const Vector3 & v,float epsilon=kNormalEpsilon)806 static bool isNormalized(const Vector3 &v, float epsilon = kNormalEpsilon)
807 {
808 return equal(length(v), 1, epsilon);
809 }
810
normalize(const Vector3 & v,float epsilon)811 static Vector3 normalize(const Vector3 &v, float epsilon)
812 {
813 float l = length(v);
814 XA_DEBUG_ASSERT(!isZero(l, epsilon));
815 XA_UNUSED(epsilon);
816 Vector3 n = v * (1.0f / l);
817 XA_DEBUG_ASSERT(isNormalized(n));
818 return n;
819 }
820
normalizeSafe(const Vector3 & v,const Vector3 & fallback,float epsilon)821 static Vector3 normalizeSafe(const Vector3 &v, const Vector3 &fallback, float epsilon)
822 {
823 float l = length(v);
824 if (isZero(l, epsilon)) {
825 return fallback;
826 }
827 return v * (1.0f / l);
828 }
829
equal(const Vector3 & v0,const Vector3 & v1,float epsilon)830 static bool equal(const Vector3 &v0, const Vector3 &v1, float epsilon)
831 {
832 return fabs(v0.x - v1.x) <= epsilon && fabs(v0.y - v1.y) <= epsilon && fabs(v0.z - v1.z) <= epsilon;
833 }
834
min(const Vector3 & a,const Vector3 & b)835 static Vector3 min(const Vector3 &a, const Vector3 &b)
836 {
837 return Vector3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
838 }
839
max(const Vector3 & a,const Vector3 & b)840 static Vector3 max(const Vector3 &a, const Vector3 &b)
841 {
842 return Vector3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
843 }
844
845 #if XA_DEBUG
isFinite(const Vector3 & v)846 bool isFinite(const Vector3 &v)
847 {
848 return isFinite(v.x) && isFinite(v.y) && isFinite(v.z);
849 }
850 #endif
851
852 struct Extents2
853 {
854 Vector2 min, max;
855
Extents2xatlas::internal::Extents2856 Extents2() {}
857
Extents2xatlas::internal::Extents2858 Extents2(Vector2 p1, Vector2 p2)
859 {
860 min = xatlas::internal::min(p1, p2);
861 max = xatlas::internal::max(p1, p2);
862 }
863
resetxatlas::internal::Extents2864 void reset()
865 {
866 min.x = min.y = FLT_MAX;
867 max.x = max.y = -FLT_MAX;
868 }
869
addxatlas::internal::Extents2870 void add(Vector2 p)
871 {
872 min = xatlas::internal::min(min, p);
873 max = xatlas::internal::max(max, p);
874 }
875
midpointxatlas::internal::Extents2876 Vector2 midpoint() const
877 {
878 return Vector2(min.x + (max.x - min.x) * 0.5f, min.y + (max.y - min.y) * 0.5f);
879 }
880
intersectxatlas::internal::Extents2881 static bool intersect(const Extents2 &e1, const Extents2 &e2)
882 {
883 return e1.min.x <= e2.max.x && e1.max.x >= e2.min.x && e1.min.y <= e2.max.y && e1.max.y >= e2.min.y;
884 }
885 };
886
887 struct Plane
888 {
889 Plane() = default;
890
Planexatlas::internal::Plane891 Plane(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3)
892 {
893 normal = cross(p2 - p1, p3 - p1);
894 dist = dot(normal, p1);
895 }
896
distancexatlas::internal::Plane897 float distance(const Vector3 &p) const
898 {
899 return dot(normal, p) - dist;
900 }
901
normalizexatlas::internal::Plane902 void normalize()
903 {
904 const float len = length(normal);
905 if (len > 0.0f) {
906 const float il = 1.0f / len;
907 normal *= il;
908 dist *= il;
909 }
910 }
911
912 Vector3 normal;
913 float dist;
914 };
915
lineIntersectsPoint(const Vector3 & point,const Vector3 & lineStart,const Vector3 & lineEnd,float * t,float epsilon)916 static bool lineIntersectsPoint(const Vector3 &point, const Vector3 &lineStart, const Vector3 &lineEnd, float *t, float epsilon)
917 {
918 float tt;
919 if (!t)
920 t = &tt;
921 *t = 0.0f;
922 if (equal(lineStart, point, epsilon) || equal(lineEnd, point, epsilon))
923 return false; // Vertex lies on either line vertices.
924 const Vector3 v01 = point - lineStart;
925 const Vector3 v21 = lineEnd - lineStart;
926 const float l = length(v21);
927 const float d = length(cross(v01, v21)) / l;
928 if (!isZero(d, epsilon))
929 return false;
930 *t = dot(v01, v21) / (l * l);
931 return *t > kEpsilon && *t < 1.0f - kEpsilon;
932 }
933
sameSide(const Vector3 & p1,const Vector3 & p2,const Vector3 & a,const Vector3 & b)934 static bool sameSide(const Vector3 &p1, const Vector3 &p2, const Vector3 &a, const Vector3 &b)
935 {
936 const Vector3 &ab = b - a;
937 return dot(cross(ab, p1 - a), cross(ab, p2 - a)) >= 0.0f;
938 }
939
940 // http://blackpawn.com/texts/pointinpoly/default.html
pointInTriangle(const Vector3 & p,const Vector3 & a,const Vector3 & b,const Vector3 & c)941 static bool pointInTriangle(const Vector3 &p, const Vector3 &a, const Vector3 &b, const Vector3 &c)
942 {
943 return sameSide(p, a, b, c) && sameSide(p, b, a, c) && sameSide(p, c, a, b);
944 }
945
946 #if XA_CLOSE_HOLES_CHECK_EDGE_INTERSECTION
947 // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
rayIntersectsTriangle(const Vector3 & rayOrigin,const Vector3 & rayDir,const Vector3 * tri,float * t)948 static bool rayIntersectsTriangle(const Vector3 &rayOrigin, const Vector3 &rayDir, const Vector3 *tri, float *t)
949 {
950 *t = 0.0f;
951 const Vector3 &edge1 = tri[1] - tri[0];
952 const Vector3 &edge2 = tri[2] - tri[0];
953 const Vector3 h = cross(rayDir, edge2);
954 const float a = dot(edge1, h);
955 if (a > -kEpsilon && a < kEpsilon)
956 return false; // This ray is parallel to this triangle.
957 const float f = 1.0f / a;
958 const Vector3 s = rayOrigin - tri[0];
959 const float u = f * dot(s, h);
960 if (u < 0.0f || u > 1.0f)
961 return false;
962 const Vector3 q = cross(s, edge1);
963 const float v = f * dot(rayDir, q);
964 if (v < 0.0f || u + v > 1.0f)
965 return false;
966 // At this stage we can compute t to find out where the intersection point is on the line.
967 *t = f * dot(edge2, q);
968 if (*t > kEpsilon && *t < 1.0f - kEpsilon)
969 return true;
970 // This means that there is a line intersection but not a ray intersection.
971 return false;
972 }
973 #endif
974
975 // From Fast-BVH
976 struct AABB
977 {
AABBxatlas::internal::AABB978 AABB() : min(FLT_MAX, FLT_MAX, FLT_MAX), max(-FLT_MAX, -FLT_MAX, -FLT_MAX) {}
AABBxatlas::internal::AABB979 AABB(const Vector3 &min, const Vector3 &max) : min(min), max(max) { }
AABBxatlas::internal::AABB980 AABB(const Vector3 &p, float radius = 0.0f) : min(p), max(p) { if (radius > 0.0f) expand(radius); }
981
intersectxatlas::internal::AABB982 bool intersect(const AABB &other) const
983 {
984 return min.x <= other.max.x && max.x >= other.min.x && min.y <= other.max.y && max.y >= other.min.y && min.z <= other.max.z && max.z >= other.min.z;
985 }
986
expandToIncludexatlas::internal::AABB987 void expandToInclude(const Vector3 &p)
988 {
989 min = internal::min(min, p);
990 max = internal::max(max, p);
991 }
992
expandToIncludexatlas::internal::AABB993 void expandToInclude(const AABB &aabb)
994 {
995 min = internal::min(min, aabb.min);
996 max = internal::max(max, aabb.max);
997 }
998
expandxatlas::internal::AABB999 void expand(float amount)
1000 {
1001 min -= Vector3(amount);
1002 max += Vector3(amount);
1003 }
1004
centroidxatlas::internal::AABB1005 Vector3 centroid() const
1006 {
1007 return min + (max - min) * 0.5f;
1008 }
1009
maxDimensionxatlas::internal::AABB1010 uint32_t maxDimension() const
1011 {
1012 const Vector3 extent = max - min;
1013 uint32_t result = 0;
1014 if (extent.y > extent.x) {
1015 result = 1;
1016 if (extent.z > extent.y)
1017 result = 2;
1018 }
1019 else if(extent.z > extent.x)
1020 result = 2;
1021 return result;
1022 }
1023
1024 Vector3 min, max;
1025 };
1026
1027 struct ArrayBase
1028 {
ArrayBasexatlas::internal::ArrayBase1029 ArrayBase(uint32_t elementSize, int memTag = MemTag::Default) : buffer(nullptr), elementSize(elementSize), size(0), capacity(0)
1030 {
1031 #if XA_DEBUG_HEAP
1032 this->memTag = memTag;
1033 #else
1034 XA_UNUSED(memTag);
1035 #endif
1036 }
1037
~ArrayBasexatlas::internal::ArrayBase1038 ~ArrayBase()
1039 {
1040 XA_FREE(buffer);
1041 }
1042
clearxatlas::internal::ArrayBase1043 XA_INLINE void clear()
1044 {
1045 size = 0;
1046 }
1047
copyFromxatlas::internal::ArrayBase1048 void copyFrom(const uint8_t *data, uint32_t length)
1049 {
1050 resize(length, true);
1051 memcpy(buffer, data, length * elementSize);
1052 }
1053
copyToxatlas::internal::ArrayBase1054 void copyTo(ArrayBase &other) const
1055 {
1056 XA_DEBUG_ASSERT(elementSize == other.elementSize);
1057 other.resize(size, true);
1058 memcpy(other.buffer, buffer, size * elementSize);
1059 }
1060
destroyxatlas::internal::ArrayBase1061 void destroy()
1062 {
1063 size = 0;
1064 XA_FREE(buffer);
1065 buffer = nullptr;
1066 capacity = 0;
1067 size = 0;
1068 }
1069
1070 // Insert the given element at the given index shifting all the elements up.
insertAtxatlas::internal::ArrayBase1071 void insertAt(uint32_t index, const uint8_t *value)
1072 {
1073 XA_DEBUG_ASSERT(index >= 0 && index <= size);
1074 resize(size + 1, false);
1075 if (index < size - 1)
1076 memmove(buffer + elementSize * (index + 1), buffer + elementSize * index, elementSize * (size - 1 - index));
1077 memcpy(&buffer[index * elementSize], value, elementSize);
1078 }
1079
moveToxatlas::internal::ArrayBase1080 void moveTo(ArrayBase &other)
1081 {
1082 XA_DEBUG_ASSERT(elementSize == other.elementSize);
1083 other.destroy();
1084 other.buffer = buffer;
1085 other.elementSize = elementSize;
1086 other.size = size;
1087 other.capacity = capacity;
1088 #if XA_DEBUG_HEAP
1089 other.memTag = memTag;
1090 #endif
1091 buffer = nullptr;
1092 elementSize = size = capacity = 0;
1093 }
1094
pop_backxatlas::internal::ArrayBase1095 void pop_back()
1096 {
1097 XA_DEBUG_ASSERT(size > 0);
1098 resize(size - 1, false);
1099 }
1100
push_backxatlas::internal::ArrayBase1101 void push_back(const uint8_t *value)
1102 {
1103 XA_DEBUG_ASSERT(value < buffer || value >= buffer + size);
1104 resize(size + 1, false);
1105 memcpy(&buffer[(size - 1) * elementSize], value, elementSize);
1106 }
1107
push_backxatlas::internal::ArrayBase1108 void push_back(const ArrayBase &other)
1109 {
1110 XA_DEBUG_ASSERT(elementSize == other.elementSize);
1111 if (other.size == 0)
1112 return;
1113 const uint32_t oldSize = size;
1114 resize(size + other.size, false);
1115 memcpy(buffer + oldSize * elementSize, other.buffer, other.size * other.elementSize);
1116 }
1117
1118 // Remove the element at the given index. This is an expensive operation!
removeAtxatlas::internal::ArrayBase1119 void removeAt(uint32_t index)
1120 {
1121 XA_DEBUG_ASSERT(index >= 0 && index < size);
1122 if (size != 1)
1123 memmove(buffer + elementSize * index, buffer + elementSize * (index + 1), elementSize * (size - 1 - index));
1124 size--;
1125 }
1126
1127 // Element at index is swapped with the last element, then the array length is decremented.
removeAtFastxatlas::internal::ArrayBase1128 void removeAtFast(uint32_t index)
1129 {
1130 XA_DEBUG_ASSERT(index >= 0 && index < size);
1131 if (size != 1 && index != size - 1)
1132 memcpy(buffer + elementSize * index, buffer + elementSize * (size - 1), elementSize);
1133 size--;
1134 }
1135
reservexatlas::internal::ArrayBase1136 void reserve(uint32_t desiredSize)
1137 {
1138 if (desiredSize > capacity)
1139 setArrayCapacity(desiredSize);
1140 }
1141
resizexatlas::internal::ArrayBase1142 void resize(uint32_t newSize, bool exact)
1143 {
1144 size = newSize;
1145 if (size > capacity) {
1146 // First allocation is always exact. Otherwise, following allocations grow array to 150% of desired size.
1147 uint32_t newBufferSize;
1148 if (capacity == 0 || exact)
1149 newBufferSize = size;
1150 else
1151 newBufferSize = size + (size >> 2);
1152 setArrayCapacity(newBufferSize);
1153 }
1154 }
1155
setArrayCapacityxatlas::internal::ArrayBase1156 void setArrayCapacity(uint32_t newCapacity)
1157 {
1158 XA_DEBUG_ASSERT(newCapacity >= size);
1159 if (newCapacity == 0) {
1160 // free the buffer.
1161 if (buffer != nullptr) {
1162 XA_FREE(buffer);
1163 buffer = nullptr;
1164 }
1165 } else {
1166 // realloc the buffer
1167 #if XA_DEBUG_HEAP
1168 buffer = XA_REALLOC_SIZE(memTag, buffer, newCapacity * elementSize);
1169 #else
1170 buffer = XA_REALLOC_SIZE(MemTag::Default, buffer, newCapacity * elementSize);
1171 #endif
1172 }
1173 capacity = newCapacity;
1174 }
1175
1176 #if XA_DEBUG_HEAP
setMemTagxatlas::internal::ArrayBase1177 void setMemTag(int _memTag)
1178 {
1179 this->memTag = _memTag;
1180 }
1181 #endif
1182
1183 uint8_t *buffer;
1184 uint32_t elementSize;
1185 uint32_t size;
1186 uint32_t capacity;
1187 #if XA_DEBUG_HEAP
1188 int memTag;
1189 #endif
1190 };
1191
1192 template<typename T>
1193 class Array
1194 {
1195 public:
Array(int memTag=MemTag::Default)1196 Array(int memTag = MemTag::Default) : m_base(sizeof(T), memTag) {}
1197 Array(const Array&) = delete;
1198 Array &operator=(const Array &) = delete;
1199
operator [](uint32_t index) const1200 XA_INLINE const T &operator[](uint32_t index) const
1201 {
1202 XA_DEBUG_ASSERT(index < m_base.size);
1203 return ((const T *)m_base.buffer)[index];
1204 }
1205
operator [](uint32_t index)1206 XA_INLINE T &operator[](uint32_t index)
1207 {
1208 XA_DEBUG_ASSERT(index < m_base.size);
1209 return ((T *)m_base.buffer)[index];
1210 }
1211
back() const1212 XA_INLINE const T &back() const
1213 {
1214 XA_DEBUG_ASSERT(!isEmpty());
1215 return ((const T *)m_base.buffer)[m_base.size - 1];
1216 }
1217
begin()1218 XA_INLINE T *begin() { return (T *)m_base.buffer; }
clear()1219 XA_INLINE void clear() { m_base.clear(); }
1220
contains(const T & value) const1221 bool contains(const T &value) const
1222 {
1223 for (uint32_t i = 0; i < m_base.size; i++) {
1224 if (((const T *)m_base.buffer)[i] == value)
1225 return true;
1226 }
1227 return false;
1228 }
1229
copyFrom(const T * data,uint32_t length)1230 void copyFrom(const T *data, uint32_t length) { m_base.copyFrom((const uint8_t *)data, length); }
copyTo(Array & other) const1231 void copyTo(Array &other) const { m_base.copyTo(other.m_base); }
data() const1232 XA_INLINE const T *data() const { return (const T *)m_base.buffer; }
data()1233 XA_INLINE T *data() { return (T *)m_base.buffer; }
destroy()1234 void destroy() { m_base.destroy(); }
end()1235 XA_INLINE T *end() { return (T *)m_base.buffer + m_base.size; }
isEmpty() const1236 XA_INLINE bool isEmpty() const { return m_base.size == 0; }
insertAt(uint32_t index,const T & value)1237 void insertAt(uint32_t index, const T &value) { m_base.insertAt(index, (const uint8_t *)&value); }
moveTo(Array & other)1238 void moveTo(Array &other) { m_base.moveTo(other.m_base); }
push_back(const T & value)1239 void push_back(const T &value) { m_base.push_back((const uint8_t *)&value); }
push_back(const Array & other)1240 void push_back(const Array &other) { m_base.push_back(other.m_base); }
pop_back()1241 void pop_back() { m_base.pop_back(); }
removeAt(uint32_t index)1242 void removeAt(uint32_t index) { m_base.removeAt(index); }
removeAtFast(uint32_t index)1243 void removeAtFast(uint32_t index) { m_base.removeAtFast(index); }
reserve(uint32_t desiredSize)1244 void reserve(uint32_t desiredSize) { m_base.reserve(desiredSize); }
resize(uint32_t newSize)1245 void resize(uint32_t newSize) { m_base.resize(newSize, true); }
1246
runCtors()1247 void runCtors()
1248 {
1249 for (uint32_t i = 0; i < m_base.size; i++)
1250 new (&((T *)m_base.buffer)[i]) T;
1251 }
1252
runDtors()1253 void runDtors()
1254 {
1255 for (uint32_t i = 0; i < m_base.size; i++)
1256 ((T *)m_base.buffer)[i].~T();
1257 }
1258
fill(const T & value)1259 void fill(const T &value)
1260 {
1261 auto buffer = (T *)m_base.buffer;
1262 for (uint32_t i = 0; i < m_base.size; i++)
1263 buffer[i] = value;
1264 }
1265
fillBytes(uint8_t value)1266 void fillBytes(uint8_t value)
1267 {
1268 memset(m_base.buffer, (int)value, m_base.size * m_base.elementSize);
1269 }
1270
1271 #if XA_DEBUG_HEAP
setMemTag(int memTag)1272 void setMemTag(int memTag) { m_base.setMemTag(memTag); }
1273 #endif
1274
size() const1275 XA_INLINE uint32_t size() const { return m_base.size; }
zeroOutMemory()1276 XA_INLINE void zeroOutMemory() { memset(m_base.buffer, 0, m_base.elementSize * m_base.size); }
1277
1278 private:
1279 ArrayBase m_base;
1280 };
1281
1282 template<typename T>
1283 struct ArrayView
1284 {
ArrayViewxatlas::internal::ArrayView1285 ArrayView() : data(nullptr), length(0) {}
ArrayViewxatlas::internal::ArrayView1286 ArrayView(Array<T> &a) : data(a.data()), length(a.size()) {}
ArrayViewxatlas::internal::ArrayView1287 ArrayView(T *data, uint32_t length) : data(data), length(length) {}
operator =xatlas::internal::ArrayView1288 ArrayView &operator=(Array<T> &a) { data = a.data(); length = a.size(); return *this; }
operator []xatlas::internal::ArrayView1289 XA_INLINE const T &operator[](uint32_t index) const { XA_DEBUG_ASSERT(index < length); return data[index]; }
1290 T *data;
1291 uint32_t length;
1292 };
1293
1294 template<typename T>
1295 struct ConstArrayView
1296 {
ConstArrayViewxatlas::internal::ConstArrayView1297 ConstArrayView() : data(nullptr), length(0) {}
ConstArrayViewxatlas::internal::ConstArrayView1298 ConstArrayView(const Array<T> &a) : data(a.data()), length(a.size()) {}
ConstArrayViewxatlas::internal::ConstArrayView1299 ConstArrayView(const T *data, uint32_t length) : data(data), length(length) {}
operator =xatlas::internal::ConstArrayView1300 ConstArrayView &operator=(const Array<T> &a) { data = a.data(); length = a.size(); return *this; }
operator []xatlas::internal::ConstArrayView1301 XA_INLINE const T &operator[](uint32_t index) const { XA_DEBUG_ASSERT(index < length); return data[index]; }
1302 const T *data;
1303 uint32_t length;
1304 };
1305
1306 /// Basis class to compute tangent space basis, ortogonalizations and to transform vectors from one space to another.
1307 struct Basis
1308 {
computeTangentxatlas::internal::Basis1309 XA_NODISCARD static Vector3 computeTangent(const Vector3 &normal)
1310 {
1311 XA_ASSERT(isNormalized(normal));
1312 // Choose minimum axis.
1313 Vector3 tangent;
1314 if (fabsf(normal.x) < fabsf(normal.y) && fabsf(normal.x) < fabsf(normal.z))
1315 tangent = Vector3(1, 0, 0);
1316 else if (fabsf(normal.y) < fabsf(normal.z))
1317 tangent = Vector3(0, 1, 0);
1318 else
1319 tangent = Vector3(0, 0, 1);
1320 // Ortogonalize
1321 tangent -= normal * dot(normal, tangent);
1322 tangent = normalize(tangent, kEpsilon);
1323 return tangent;
1324 }
1325
computeBitangentxatlas::internal::Basis1326 XA_NODISCARD static Vector3 computeBitangent(const Vector3 &normal, const Vector3 &tangent)
1327 {
1328 return cross(normal, tangent);
1329 }
1330
1331 Vector3 tangent = Vector3(0.0f);
1332 Vector3 bitangent = Vector3(0.0f);
1333 Vector3 normal = Vector3(0.0f);
1334 };
1335
1336 // Simple bit array.
1337 class BitArray
1338 {
1339 public:
BitArray()1340 BitArray() : m_size(0) {}
1341
BitArray(uint32_t sz)1342 BitArray(uint32_t sz)
1343 {
1344 resize(sz);
1345 }
1346
resize(uint32_t new_size)1347 void resize(uint32_t new_size)
1348 {
1349 m_size = new_size;
1350 m_wordArray.resize((m_size + 31) >> 5);
1351 }
1352
get(uint32_t index) const1353 bool get(uint32_t index) const
1354 {
1355 XA_DEBUG_ASSERT(index < m_size);
1356 return (m_wordArray[index >> 5] & (1 << (index & 31))) != 0;
1357 }
1358
set(uint32_t index)1359 void set(uint32_t index)
1360 {
1361 XA_DEBUG_ASSERT(index < m_size);
1362 m_wordArray[index >> 5] |= (1 << (index & 31));
1363 }
1364
unset(uint32_t index)1365 void unset(uint32_t index)
1366 {
1367 XA_DEBUG_ASSERT(index < m_size);
1368 m_wordArray[index >> 5] &= ~(1 << (index & 31));
1369 }
1370
zeroOutMemory()1371 void zeroOutMemory()
1372 {
1373 m_wordArray.zeroOutMemory();
1374 }
1375
1376 private:
1377 uint32_t m_size; // Number of bits stored.
1378 Array<uint32_t> m_wordArray;
1379 };
1380
1381 class BitImage
1382 {
1383 public:
BitImage()1384 BitImage() : m_width(0), m_height(0), m_rowStride(0), m_data(MemTag::BitImage) {}
1385
BitImage(uint32_t w,uint32_t h)1386 BitImage(uint32_t w, uint32_t h) : m_width(w), m_height(h), m_data(MemTag::BitImage)
1387 {
1388 m_rowStride = (m_width + 63) >> 6;
1389 m_data.resize(m_rowStride * m_height);
1390 m_data.zeroOutMemory();
1391 }
1392
1393 BitImage(const BitImage &other) = delete;
1394 BitImage &operator=(const BitImage &other) = delete;
width() const1395 uint32_t width() const { return m_width; }
height() const1396 uint32_t height() const { return m_height; }
1397
copyTo(BitImage & other)1398 void copyTo(BitImage &other)
1399 {
1400 other.m_width = m_width;
1401 other.m_height = m_height;
1402 other.m_rowStride = m_rowStride;
1403 m_data.copyTo(other.m_data);
1404 }
1405
resize(uint32_t w,uint32_t h,bool discard)1406 void resize(uint32_t w, uint32_t h, bool discard)
1407 {
1408 const uint32_t rowStride = (w + 63) >> 6;
1409 if (discard) {
1410 m_data.resize(rowStride * h);
1411 m_data.zeroOutMemory();
1412 } else {
1413 Array<uint64_t> tmp;
1414 tmp.resize(rowStride * h);
1415 memset(tmp.data(), 0, tmp.size() * sizeof(uint64_t));
1416 // If only height has changed, can copy all rows at once.
1417 if (rowStride == m_rowStride) {
1418 memcpy(tmp.data(), m_data.data(), m_rowStride * min(m_height, h) * sizeof(uint64_t));
1419 } else if (m_width > 0 && m_height > 0) {
1420 const uint32_t height = min(m_height, h);
1421 for (uint32_t i = 0; i < height; i++)
1422 memcpy(&tmp[i * rowStride], &m_data[i * m_rowStride], min(rowStride, m_rowStride) * sizeof(uint64_t));
1423 }
1424 tmp.moveTo(m_data);
1425 }
1426 m_width = w;
1427 m_height = h;
1428 m_rowStride = rowStride;
1429 }
1430
get(uint32_t x,uint32_t y) const1431 bool get(uint32_t x, uint32_t y) const
1432 {
1433 XA_DEBUG_ASSERT(x < m_width && y < m_height);
1434 const uint32_t index = (x >> 6) + y * m_rowStride;
1435 return (m_data[index] & (UINT64_C(1) << (uint64_t(x) & UINT64_C(63)))) != 0;
1436 }
1437
set(uint32_t x,uint32_t y)1438 void set(uint32_t x, uint32_t y)
1439 {
1440 XA_DEBUG_ASSERT(x < m_width && y < m_height);
1441 const uint32_t index = (x >> 6) + y * m_rowStride;
1442 m_data[index] |= UINT64_C(1) << (uint64_t(x) & UINT64_C(63));
1443 XA_DEBUG_ASSERT(get(x, y));
1444 }
1445
zeroOutMemory()1446 void zeroOutMemory()
1447 {
1448 m_data.zeroOutMemory();
1449 }
1450
canBlit(const BitImage & image,uint32_t offsetX,uint32_t offsetY) const1451 bool canBlit(const BitImage &image, uint32_t offsetX, uint32_t offsetY) const
1452 {
1453 for (uint32_t y = 0; y < image.m_height; y++) {
1454 const uint32_t thisY = y + offsetY;
1455 if (thisY >= m_height)
1456 continue;
1457 uint32_t x = 0;
1458 for (;;) {
1459 const uint32_t thisX = x + offsetX;
1460 if (thisX >= m_width)
1461 break;
1462 const uint32_t thisBlockShift = thisX % 64;
1463 const uint64_t thisBlock = m_data[(thisX >> 6) + thisY * m_rowStride] >> thisBlockShift;
1464 const uint32_t blockShift = x % 64;
1465 const uint64_t block = image.m_data[(x >> 6) + y * image.m_rowStride] >> blockShift;
1466 if ((thisBlock & block) != 0)
1467 return false;
1468 x += 64 - max(thisBlockShift, blockShift);
1469 if (x >= image.m_width)
1470 break;
1471 }
1472 }
1473 return true;
1474 }
1475
dilate(uint32_t padding)1476 void dilate(uint32_t padding)
1477 {
1478 BitImage tmp(m_width, m_height);
1479 for (uint32_t p = 0; p < padding; p++) {
1480 tmp.zeroOutMemory();
1481 for (uint32_t y = 0; y < m_height; y++) {
1482 for (uint32_t x = 0; x < m_width; x++) {
1483 bool b = get(x, y);
1484 if (!b) {
1485 if (x > 0) {
1486 b |= get(x - 1, y);
1487 if (y > 0) b |= get(x - 1, y - 1);
1488 if (y < m_height - 1) b |= get(x - 1, y + 1);
1489 }
1490 if (y > 0) b |= get(x, y - 1);
1491 if (y < m_height - 1) b |= get(x, y + 1);
1492 if (x < m_width - 1) {
1493 b |= get(x + 1, y);
1494 if (y > 0) b |= get(x + 1, y - 1);
1495 if (y < m_height - 1) b |= get(x + 1, y + 1);
1496 }
1497 }
1498 if (b)
1499 tmp.set(x, y);
1500 }
1501 }
1502 tmp.m_data.copyTo(m_data);
1503 }
1504 }
1505
1506 private:
1507 uint32_t m_width;
1508 uint32_t m_height;
1509 uint32_t m_rowStride; // In uint64_t's
1510 Array<uint64_t> m_data;
1511 };
1512
1513 // From Fast-BVH
1514 class BVH
1515 {
1516 public:
BVH(const Array<AABB> & objectAabbs,uint32_t leafSize=4)1517 BVH(const Array<AABB> &objectAabbs, uint32_t leafSize = 4) : m_objectIds(MemTag::BVH), m_nodes(MemTag::BVH)
1518 {
1519 m_objectAabbs = &objectAabbs;
1520 if (m_objectAabbs->isEmpty())
1521 return;
1522 m_objectIds.resize(objectAabbs.size());
1523 for (uint32_t i = 0; i < m_objectIds.size(); i++)
1524 m_objectIds[i] = i;
1525 BuildEntry todo[128];
1526 uint32_t stackptr = 0;
1527 const uint32_t kRoot = 0xfffffffc;
1528 const uint32_t kUntouched = 0xffffffff;
1529 const uint32_t kTouchedTwice = 0xfffffffd;
1530 // Push the root
1531 todo[stackptr].start = 0;
1532 todo[stackptr].end = objectAabbs.size();
1533 todo[stackptr].parent = kRoot;
1534 stackptr++;
1535 Node node;
1536 m_nodes.reserve(objectAabbs.size() * 2);
1537 uint32_t nNodes = 0;
1538 while(stackptr > 0) {
1539 // Pop the next item off of the stack
1540 const BuildEntry &bnode = todo[--stackptr];
1541 const uint32_t start = bnode.start;
1542 const uint32_t end = bnode.end;
1543 const uint32_t nPrims = end - start;
1544 nNodes++;
1545 node.start = start;
1546 node.nPrims = nPrims;
1547 node.rightOffset = kUntouched;
1548 // Calculate the bounding box for this node
1549 AABB bb(objectAabbs[m_objectIds[start]]);
1550 AABB bc(objectAabbs[m_objectIds[start]].centroid());
1551 for(uint32_t p = start + 1; p < end; ++p) {
1552 bb.expandToInclude(objectAabbs[m_objectIds[p]]);
1553 bc.expandToInclude(objectAabbs[m_objectIds[p]].centroid());
1554 }
1555 node.aabb = bb;
1556 // If the number of primitives at this point is less than the leaf
1557 // size, then this will become a leaf. (Signified by rightOffset == 0)
1558 if (nPrims <= leafSize)
1559 node.rightOffset = 0;
1560 m_nodes.push_back(node);
1561 // Child touches parent...
1562 // Special case: Don't do this for the root.
1563 if (bnode.parent != kRoot) {
1564 m_nodes[bnode.parent].rightOffset--;
1565 // When this is the second touch, this is the right child.
1566 // The right child sets up the offset for the flat tree.
1567 if (m_nodes[bnode.parent].rightOffset == kTouchedTwice )
1568 m_nodes[bnode.parent].rightOffset = nNodes - 1 - bnode.parent;
1569 }
1570 // If this is a leaf, no need to subdivide.
1571 if (node.rightOffset == 0)
1572 continue;
1573 // Set the split dimensions
1574 const uint32_t split_dim = bc.maxDimension();
1575 // Split on the center of the longest axis
1576 const float split_coord = 0.5f * ((&bc.min.x)[split_dim] + (&bc.max.x)[split_dim]);
1577 // Partition the list of objects on this split
1578 uint32_t mid = start;
1579 for (uint32_t i = start; i < end; ++i) {
1580 const Vector3 centroid(objectAabbs[m_objectIds[i]].centroid());
1581 if ((¢roid.x)[split_dim] < split_coord) {
1582 swap(m_objectIds[i], m_objectIds[mid]);
1583 ++mid;
1584 }
1585 }
1586 // If we get a bad split, just choose the center...
1587 if (mid == start || mid == end)
1588 mid = start + (end - start) / 2;
1589 // Push right child
1590 todo[stackptr].start = mid;
1591 todo[stackptr].end = end;
1592 todo[stackptr].parent = nNodes - 1;
1593 stackptr++;
1594 // Push left child
1595 todo[stackptr].start = start;
1596 todo[stackptr].end = mid;
1597 todo[stackptr].parent = nNodes - 1;
1598 stackptr++;
1599 }
1600 }
1601
query(const AABB & queryAabb,Array<uint32_t> & result) const1602 void query(const AABB &queryAabb, Array<uint32_t> &result) const
1603 {
1604 result.clear();
1605 // Working set
1606 uint32_t todo[64];
1607 int32_t stackptr = 0;
1608 // "Push" on the root node to the working set
1609 todo[stackptr] = 0;
1610 while(stackptr >= 0) {
1611 // Pop off the next node to work on.
1612 const int ni = todo[stackptr--];
1613 const Node &node = m_nodes[ni];
1614 // Is leaf -> Intersect
1615 if (node.rightOffset == 0) {
1616 for(uint32_t o = 0; o < node.nPrims; ++o) {
1617 const uint32_t obj = node.start + o;
1618 if (queryAabb.intersect((*m_objectAabbs)[m_objectIds[obj]]))
1619 result.push_back(m_objectIds[obj]);
1620 }
1621 } else { // Not a leaf
1622 const uint32_t left = ni + 1;
1623 const uint32_t right = ni + node.rightOffset;
1624 if (queryAabb.intersect(m_nodes[left].aabb))
1625 todo[++stackptr] = left;
1626 if (queryAabb.intersect(m_nodes[right].aabb))
1627 todo[++stackptr] = right;
1628 }
1629 }
1630 }
1631
1632 private:
1633 struct BuildEntry
1634 {
1635 uint32_t parent; // If non-zero then this is the index of the parent. (used in offsets)
1636 uint32_t start, end; // The range of objects in the object list covered by this node.
1637 };
1638
1639 struct Node
1640 {
1641 AABB aabb;
1642 uint32_t start, nPrims, rightOffset;
1643 };
1644
1645 const Array<AABB> *m_objectAabbs;
1646 Array<uint32_t> m_objectIds;
1647 Array<Node> m_nodes;
1648 };
1649
1650 struct Fit
1651 {
computeBasisxatlas::internal::Fit1652 static bool computeBasis(const Vector3 *points, uint32_t pointsCount, Basis *basis)
1653 {
1654 if (computeLeastSquaresNormal(points, pointsCount, &basis->normal)) {
1655 basis->tangent = Basis::computeTangent(basis->normal);
1656 basis->bitangent = Basis::computeBitangent(basis->normal, basis->tangent);
1657 return true;
1658 }
1659 return computeEigen(points, pointsCount, basis);
1660 }
1661
1662 private:
1663 // Fit a plane to a collection of points.
1664 // Fast, and accurate to within a few degrees.
1665 // Returns None if the points do not span a plane.
1666 // https://www.ilikebigbits.com/2015_03_04_plane_from_points.html
computeLeastSquaresNormalxatlas::internal::Fit1667 static bool computeLeastSquaresNormal(const Vector3 *points, uint32_t pointsCount, Vector3 *normal)
1668 {
1669 XA_DEBUG_ASSERT(pointsCount >= 3);
1670 if (pointsCount == 3) {
1671 *normal = normalize(cross(points[2] - points[0], points[1] - points[0]), kEpsilon);
1672 return true;
1673 }
1674 const float invN = 1.0f / float(pointsCount);
1675 Vector3 centroid(0.0f);
1676 for (uint32_t i = 0; i < pointsCount; i++)
1677 centroid += points[i];
1678 centroid *= invN;
1679 // Calculate full 3x3 covariance matrix, excluding symmetries:
1680 float xx = 0.0f, xy = 0.0f, xz = 0.0f, yy = 0.0f, yz = 0.0f, zz = 0.0f;
1681 for (uint32_t i = 0; i < pointsCount; i++) {
1682 Vector3 r = points[i] - centroid;
1683 xx += r.x * r.x;
1684 xy += r.x * r.y;
1685 xz += r.x * r.z;
1686 yy += r.y * r.y;
1687 yz += r.y * r.z;
1688 zz += r.z * r.z;
1689 }
1690 #if 0
1691 xx *= invN;
1692 xy *= invN;
1693 xz *= invN;
1694 yy *= invN;
1695 yz *= invN;
1696 zz *= invN;
1697 Vector3 weighted_dir(0.0f);
1698 {
1699 float det_x = yy * zz - yz * yz;
1700 const Vector3 axis_dir(det_x, xz * yz - xy * zz, xy * yz - xz * yy);
1701 float weight = det_x * det_x;
1702 if (dot(weighted_dir, axis_dir) < 0.0f)
1703 weight = -weight;
1704 weighted_dir += axis_dir * weight;
1705 }
1706 {
1707 float det_y = xx * zz - xz * xz;
1708 const Vector3 axis_dir(xz * yz - xy * zz, det_y, xy * xz - yz * xx);
1709 float weight = det_y * det_y;
1710 if (dot(weighted_dir, axis_dir) < 0.0f)
1711 weight = -weight;
1712 weighted_dir += axis_dir * weight;
1713 }
1714 {
1715 float det_z = xx * yy - xy * xy;
1716 const Vector3 axis_dir(xy * yz - xz * yy, xy * xz - yz * xx, det_z);
1717 float weight = det_z * det_z;
1718 if (dot(weighted_dir, axis_dir) < 0.0f)
1719 weight = -weight;
1720 weighted_dir += axis_dir * weight;
1721 }
1722 *normal = normalize(weighted_dir, kEpsilon);
1723 #else
1724 const float det_x = yy * zz - yz * yz;
1725 const float det_y = xx * zz - xz * xz;
1726 const float det_z = xx * yy - xy * xy;
1727 const float det_max = max(det_x, max(det_y, det_z));
1728 if (det_max <= 0.0f)
1729 return false; // The points don't span a plane
1730 // Pick path with best conditioning:
1731 Vector3 dir(0.0f);
1732 if (det_max == det_x)
1733 dir = Vector3(det_x,xz * yz - xy * zz,xy * yz - xz * yy);
1734 else if (det_max == det_y)
1735 dir = Vector3(xz * yz - xy * zz, det_y, xy * xz - yz * xx);
1736 else if (det_max == det_z)
1737 dir = Vector3(xy * yz - xz * yy, xy * xz - yz * xx, det_z);
1738 const float len = length(dir);
1739 if (isZero(len, kEpsilon))
1740 return false;
1741 *normal = dir * (1.0f / len);
1742 #endif
1743 return isNormalized(*normal);
1744 }
1745
computeEigenxatlas::internal::Fit1746 static bool computeEigen(const Vector3 *points, uint32_t pointsCount, Basis *basis)
1747 {
1748 float matrix[6];
1749 computeCovariance(pointsCount, points, matrix);
1750 if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0)
1751 return false;
1752 float eigenValues[3];
1753 Vector3 eigenVectors[3];
1754 if (!eigenSolveSymmetric3(matrix, eigenValues, eigenVectors))
1755 return false;
1756 basis->normal = normalize(eigenVectors[2], kEpsilon);
1757 basis->tangent = normalize(eigenVectors[0], kEpsilon);
1758 basis->bitangent = normalize(eigenVectors[1], kEpsilon);
1759 return true;
1760 }
1761
computeCentroidxatlas::internal::Fit1762 static Vector3 computeCentroid(int n, const Vector3 * points)
1763 {
1764 Vector3 centroid(0.0f);
1765 for (int i = 0; i < n; i++) {
1766 centroid += points[i];
1767 }
1768 centroid /= float(n);
1769 return centroid;
1770 }
1771
computeCovariancexatlas::internal::Fit1772 static Vector3 computeCovariance(int n, const Vector3 * points, float * covariance)
1773 {
1774 // compute the centroid
1775 Vector3 centroid = computeCentroid(n, points);
1776 // compute covariance matrix
1777 for (int i = 0; i < 6; i++) {
1778 covariance[i] = 0.0f;
1779 }
1780 for (int i = 0; i < n; i++) {
1781 Vector3 v = points[i] - centroid;
1782 covariance[0] += v.x * v.x;
1783 covariance[1] += v.x * v.y;
1784 covariance[2] += v.x * v.z;
1785 covariance[3] += v.y * v.y;
1786 covariance[4] += v.y * v.z;
1787 covariance[5] += v.z * v.z;
1788 }
1789 return centroid;
1790 }
1791
1792 // Tridiagonal solver from Charles Bloom.
1793 // Householder transforms followed by QL decomposition.
1794 // Seems to be based on the code from Numerical Recipes in C.
eigenSolveSymmetric3xatlas::internal::Fit1795 static bool eigenSolveSymmetric3(const float matrix[6], float eigenValues[3], Vector3 eigenVectors[3])
1796 {
1797 XA_DEBUG_ASSERT(matrix != nullptr && eigenValues != nullptr && eigenVectors != nullptr);
1798 float subd[3];
1799 float diag[3];
1800 float work[3][3];
1801 work[0][0] = matrix[0];
1802 work[0][1] = work[1][0] = matrix[1];
1803 work[0][2] = work[2][0] = matrix[2];
1804 work[1][1] = matrix[3];
1805 work[1][2] = work[2][1] = matrix[4];
1806 work[2][2] = matrix[5];
1807 EigenSolver3_Tridiagonal(work, diag, subd);
1808 if (!EigenSolver3_QLAlgorithm(work, diag, subd)) {
1809 for (int i = 0; i < 3; i++) {
1810 eigenValues[i] = 0;
1811 eigenVectors[i] = Vector3(0);
1812 }
1813 return false;
1814 }
1815 for (int i = 0; i < 3; i++) {
1816 eigenValues[i] = (float)diag[i];
1817 }
1818 // eigenvectors are the columns; make them the rows :
1819 for (int i = 0; i < 3; i++) {
1820 for (int j = 0; j < 3; j++) {
1821 (&eigenVectors[j].x)[i] = (float) work[i][j];
1822 }
1823 }
1824 // shuffle to sort by singular value :
1825 if (eigenValues[2] > eigenValues[0] && eigenValues[2] > eigenValues[1]) {
1826 swap(eigenValues[0], eigenValues[2]);
1827 swap(eigenVectors[0], eigenVectors[2]);
1828 }
1829 if (eigenValues[1] > eigenValues[0]) {
1830 swap(eigenValues[0], eigenValues[1]);
1831 swap(eigenVectors[0], eigenVectors[1]);
1832 }
1833 if (eigenValues[2] > eigenValues[1]) {
1834 swap(eigenValues[1], eigenValues[2]);
1835 swap(eigenVectors[1], eigenVectors[2]);
1836 }
1837 XA_DEBUG_ASSERT(eigenValues[0] >= eigenValues[1] && eigenValues[0] >= eigenValues[2]);
1838 XA_DEBUG_ASSERT(eigenValues[1] >= eigenValues[2]);
1839 return true;
1840 }
1841
1842 private:
EigenSolver3_Tridiagonalxatlas::internal::Fit1843 static void EigenSolver3_Tridiagonal(float mat[3][3], float *diag, float *subd)
1844 {
1845 // Householder reduction T = Q^t M Q
1846 // Input:
1847 // mat, symmetric 3x3 matrix M
1848 // Output:
1849 // mat, orthogonal matrix Q
1850 // diag, diagonal entries of T
1851 // subd, subdiagonal entries of T (T is symmetric)
1852 const float epsilon = 1e-08f;
1853 float a = mat[0][0];
1854 float b = mat[0][1];
1855 float c = mat[0][2];
1856 float d = mat[1][1];
1857 float e = mat[1][2];
1858 float f = mat[2][2];
1859 diag[0] = a;
1860 subd[2] = 0.f;
1861 if (fabsf(c) >= epsilon) {
1862 const float ell = sqrtf(b * b + c * c);
1863 b /= ell;
1864 c /= ell;
1865 const float q = 2 * b * e + c * (f - d);
1866 diag[1] = d + c * q;
1867 diag[2] = f - c * q;
1868 subd[0] = ell;
1869 subd[1] = e - b * q;
1870 mat[0][0] = 1;
1871 mat[0][1] = 0;
1872 mat[0][2] = 0;
1873 mat[1][0] = 0;
1874 mat[1][1] = b;
1875 mat[1][2] = c;
1876 mat[2][0] = 0;
1877 mat[2][1] = c;
1878 mat[2][2] = -b;
1879 } else {
1880 diag[1] = d;
1881 diag[2] = f;
1882 subd[0] = b;
1883 subd[1] = e;
1884 mat[0][0] = 1;
1885 mat[0][1] = 0;
1886 mat[0][2] = 0;
1887 mat[1][0] = 0;
1888 mat[1][1] = 1;
1889 mat[1][2] = 0;
1890 mat[2][0] = 0;
1891 mat[2][1] = 0;
1892 mat[2][2] = 1;
1893 }
1894 }
1895
EigenSolver3_QLAlgorithmxatlas::internal::Fit1896 static bool EigenSolver3_QLAlgorithm(float mat[3][3], float *diag, float *subd)
1897 {
1898 // QL iteration with implicit shifting to reduce matrix from tridiagonal
1899 // to diagonal
1900 const int maxiter = 32;
1901 for (int ell = 0; ell < 3; ell++) {
1902 int iter;
1903 for (iter = 0; iter < maxiter; iter++) {
1904 int m;
1905 for (m = ell; m <= 1; m++) {
1906 float dd = fabsf(diag[m]) + fabsf(diag[m + 1]);
1907 if ( fabsf(subd[m]) + dd == dd )
1908 break;
1909 }
1910 if ( m == ell )
1911 break;
1912 float g = (diag[ell + 1] - diag[ell]) / (2 * subd[ell]);
1913 float r = sqrtf(g * g + 1);
1914 if ( g < 0 )
1915 g = diag[m] - diag[ell] + subd[ell] / (g - r);
1916 else
1917 g = diag[m] - diag[ell] + subd[ell] / (g + r);
1918 float s = 1, c = 1, p = 0;
1919 for (int i = m - 1; i >= ell; i--) {
1920 float f = s * subd[i], b = c * subd[i];
1921 if ( fabsf(f) >= fabsf(g) ) {
1922 c = g / f;
1923 r = sqrtf(c * c + 1);
1924 subd[i + 1] = f * r;
1925 c *= (s = 1 / r);
1926 } else {
1927 s = f / g;
1928 r = sqrtf(s * s + 1);
1929 subd[i + 1] = g * r;
1930 s *= (c = 1 / r);
1931 }
1932 g = diag[i + 1] - p;
1933 r = (diag[i] - g) * s + 2 * b * c;
1934 p = s * r;
1935 diag[i + 1] = g + p;
1936 g = c * r - b;
1937 for (int k = 0; k < 3; k++) {
1938 f = mat[k][i + 1];
1939 mat[k][i + 1] = s * mat[k][i] + c * f;
1940 mat[k][i] = c * mat[k][i] - s * f;
1941 }
1942 }
1943 diag[ell] -= p;
1944 subd[ell] = g;
1945 subd[m] = 0;
1946 }
1947 if ( iter == maxiter )
1948 // should not get here under normal circumstances
1949 return false;
1950 }
1951 return true;
1952 }
1953 };
1954
sdbmHash(const void * data_in,uint32_t size,uint32_t h=5381)1955 static uint32_t sdbmHash(const void *data_in, uint32_t size, uint32_t h = 5381)
1956 {
1957 const uint8_t *data = (const uint8_t *) data_in;
1958 uint32_t i = 0;
1959 while (i < size) {
1960 h = (h << 16) + (h << 6) - h + (uint32_t ) data[i++];
1961 }
1962 return h;
1963 }
1964
1965 template <typename T>
hash(const T & t,uint32_t h=5381)1966 static uint32_t hash(const T &t, uint32_t h = 5381)
1967 {
1968 return sdbmHash(&t, sizeof(T), h);
1969 }
1970
1971 template <typename Key>
1972 struct Hash
1973 {
operator ()xatlas::internal::Hash1974 uint32_t operator()(const Key &k) const { return hash(k); }
1975 };
1976
1977 template <typename Key>
1978 struct PassthroughHash
1979 {
operator ()xatlas::internal::PassthroughHash1980 uint32_t operator()(const Key &k) const { return (uint32_t)k; }
1981 };
1982
1983 template <typename Key>
1984 struct Equal
1985 {
operator ()xatlas::internal::Equal1986 bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; }
1987 };
1988
1989 template<typename Key, typename H = Hash<Key>, typename E = Equal<Key> >
1990 class HashMap
1991 {
1992 public:
HashMap(int memTag,uint32_t size)1993 HashMap(int memTag, uint32_t size) : m_memTag(memTag), m_size(size), m_numSlots(0), m_slots(nullptr), m_keys(memTag), m_next(memTag)
1994 {
1995 }
1996
~HashMap()1997 ~HashMap()
1998 {
1999 if (m_slots)
2000 XA_FREE(m_slots);
2001 }
2002
destroy()2003 void destroy()
2004 {
2005 if (m_slots) {
2006 XA_FREE(m_slots);
2007 m_slots = nullptr;
2008 }
2009 m_keys.destroy();
2010 m_next.destroy();
2011 }
2012
add(const Key & key)2013 uint32_t add(const Key &key)
2014 {
2015 if (!m_slots)
2016 alloc();
2017 const uint32_t hash = computeHash(key);
2018 m_keys.push_back(key);
2019 m_next.push_back(m_slots[hash]);
2020 m_slots[hash] = m_next.size() - 1;
2021 return m_keys.size() - 1;
2022 }
2023
get(const Key & key) const2024 uint32_t get(const Key &key) const
2025 {
2026 if (!m_slots)
2027 return UINT32_MAX;
2028 const uint32_t hash = computeHash(key);
2029 uint32_t i = m_slots[hash];
2030 E equal;
2031 while (i != UINT32_MAX) {
2032 if (equal(m_keys[i], key))
2033 return i;
2034 i = m_next[i];
2035 }
2036 return UINT32_MAX;
2037 }
2038
getNext(uint32_t current) const2039 uint32_t getNext(uint32_t current) const
2040 {
2041 uint32_t i = m_next[current];
2042 E equal;
2043 while (i != UINT32_MAX) {
2044 if (equal(m_keys[i], m_keys[current]))
2045 return i;
2046 i = m_next[i];
2047 }
2048 return UINT32_MAX;
2049 }
2050
2051 private:
alloc()2052 void alloc()
2053 {
2054 XA_DEBUG_ASSERT(m_size > 0);
2055 m_numSlots = nextPowerOfTwo(m_size);
2056 auto minNumSlots = uint32_t(m_size * 1.3);
2057 if (m_numSlots < minNumSlots)
2058 m_numSlots = nextPowerOfTwo(minNumSlots);
2059 m_slots = XA_ALLOC_ARRAY(m_memTag, uint32_t, m_numSlots);
2060 for (uint32_t i = 0; i < m_numSlots; i++)
2061 m_slots[i] = UINT32_MAX;
2062 m_keys.reserve(m_size);
2063 m_next.reserve(m_size);
2064 }
2065
computeHash(const Key & key) const2066 uint32_t computeHash(const Key &key) const
2067 {
2068 H hash;
2069 return hash(key) & (m_numSlots - 1);
2070 }
2071
2072 int m_memTag;
2073 uint32_t m_size;
2074 uint32_t m_numSlots;
2075 uint32_t *m_slots;
2076 Array<Key> m_keys;
2077 Array<uint32_t> m_next;
2078 };
2079
2080 template<typename T>
insertionSort(T * data,uint32_t length)2081 static void insertionSort(T *data, uint32_t length)
2082 {
2083 for (int32_t i = 1; i < (int32_t)length; i++) {
2084 T x = data[i];
2085 int32_t j = i - 1;
2086 while (j >= 0 && x < data[j]) {
2087 data[j + 1] = data[j];
2088 j--;
2089 }
2090 data[j + 1] = x;
2091 }
2092 }
2093
2094 class KISSRng
2095 {
2096 public:
KISSRng()2097 KISSRng() { reset(); }
2098
reset()2099 void reset()
2100 {
2101 x = 123456789;
2102 y = 362436000;
2103 z = 521288629;
2104 c = 7654321;
2105 }
2106
getRange(uint32_t range)2107 uint32_t getRange(uint32_t range)
2108 {
2109 if (range == 0)
2110 return 0;
2111 x = 69069 * x + 12345;
2112 y ^= (y << 13);
2113 y ^= (y >> 17);
2114 y ^= (y << 5);
2115 uint64_t t = 698769069ULL * z + c;
2116 c = (t >> 32);
2117 return (x + y + (z = (uint32_t)t)) % (range + 1);
2118 }
2119
2120 private:
2121 uint32_t x, y, z, c;
2122 };
2123
2124 // Based on Pierre Terdiman's and Michael Herf's source code.
2125 // http://www.codercorner.com/RadixSortRevisited.htm
2126 // http://www.stereopsis.com/radix.html
2127 class RadixSort
2128 {
2129 public:
sort(const float * input,uint32_t count)2130 void sort(const float *input, uint32_t count)
2131 {
2132 if (input == nullptr || count == 0) {
2133 m_buffer1.clear();
2134 m_buffer2.clear();
2135 m_ranks = m_buffer1.data();
2136 m_ranks2 = m_buffer2.data();
2137 return;
2138 }
2139 // Resize lists if needed
2140 m_buffer1.resize(count);
2141 m_buffer2.resize(count);
2142 m_ranks = m_buffer1.data();
2143 m_ranks2 = m_buffer2.data();
2144 m_validRanks = false;
2145 if (count < 32)
2146 insertionSort(input, count);
2147 else {
2148 // @@ Avoid touching the input multiple times.
2149 for (uint32_t i = 0; i < count; i++) {
2150 floatFlip((uint32_t &)input[i]);
2151 }
2152 radixSort<uint32_t>((const uint32_t *)input, count);
2153 for (uint32_t i = 0; i < count; i++) {
2154 ifloatFlip((uint32_t &)input[i]);
2155 }
2156 }
2157 }
2158
sort(const Array<float> & input)2159 void sort(const Array<float> &input)
2160 {
2161 sort(input.data(), input.size());
2162 }
2163
2164 // Access to results. m_ranks is a list of indices in sorted order, i.e. in the order you may further process your data
ranks() const2165 const uint32_t *ranks() const
2166 {
2167 XA_DEBUG_ASSERT(m_validRanks);
2168 return m_ranks;
2169 }
2170
2171 private:
2172 uint32_t *m_ranks, *m_ranks2;
2173 Array<uint32_t> m_buffer1, m_buffer2;
2174 bool m_validRanks;
2175
floatFlip(uint32_t & f)2176 void floatFlip(uint32_t &f)
2177 {
2178 int32_t mask = (int32_t(f) >> 31) | 0x80000000; // Warren Hunt, Manchor Ko.
2179 f ^= mask;
2180 }
2181
ifloatFlip(uint32_t & f)2182 void ifloatFlip(uint32_t &f)
2183 {
2184 uint32_t mask = ((f >> 31) - 1) | 0x80000000; // Michael Herf.
2185 f ^= mask;
2186 }
2187
2188 template<typename T>
createHistograms(const T * buffer,uint32_t count,uint32_t * histogram)2189 void createHistograms(const T *buffer, uint32_t count, uint32_t *histogram)
2190 {
2191 const uint32_t bucketCount = sizeof(T); // (8 * sizeof(T)) / log2(radix)
2192 // Init bucket pointers.
2193 uint32_t *h[bucketCount];
2194 for (uint32_t i = 0; i < bucketCount; i++) {
2195 h[i] = histogram + 256 * i;
2196 }
2197 // Clear histograms.
2198 memset(histogram, 0, 256 * bucketCount * sizeof(uint32_t ));
2199 // @@ Add support for signed integers.
2200 // Build histograms.
2201 const uint8_t *p = (const uint8_t *)buffer; // @@ Does this break aliasing rules?
2202 const uint8_t *pe = p + count * sizeof(T);
2203 while (p != pe) {
2204 h[0][*p++]++, h[1][*p++]++, h[2][*p++]++, h[3][*p++]++;
2205 #ifdef _MSC_VER
2206 #pragma warning(push)
2207 #pragma warning(disable : 4127)
2208 #endif
2209 if (bucketCount == 8) h[4][*p++]++, h[5][*p++]++, h[6][*p++]++, h[7][*p++]++;
2210 #ifdef _MSC_VER
2211 #pragma warning(pop)
2212 #endif
2213 }
2214 }
2215
2216 template <typename T>
insertionSort(const T * input,uint32_t count)2217 void insertionSort(const T *input, uint32_t count)
2218 {
2219 if (!m_validRanks) {
2220 m_ranks[0] = 0;
2221 for (uint32_t i = 1; i != count; ++i) {
2222 int rank = m_ranks[i] = i;
2223 uint32_t j = i;
2224 while (j != 0 && input[rank] < input[m_ranks[j - 1]]) {
2225 m_ranks[j] = m_ranks[j - 1];
2226 --j;
2227 }
2228 if (i != j) {
2229 m_ranks[j] = rank;
2230 }
2231 }
2232 m_validRanks = true;
2233 } else {
2234 for (uint32_t i = 1; i != count; ++i) {
2235 int rank = m_ranks[i];
2236 uint32_t j = i;
2237 while (j != 0 && input[rank] < input[m_ranks[j - 1]]) {
2238 m_ranks[j] = m_ranks[j - 1];
2239 --j;
2240 }
2241 if (i != j) {
2242 m_ranks[j] = rank;
2243 }
2244 }
2245 }
2246 }
2247
2248 template <typename T>
radixSort(const T * input,uint32_t count)2249 void radixSort(const T *input, uint32_t count)
2250 {
2251 const uint32_t P = sizeof(T); // pass count
2252 // Allocate histograms & offsets on the stack
2253 uint32_t histogram[256 * P];
2254 uint32_t *link[256];
2255 createHistograms(input, count, histogram);
2256 // Radix sort, j is the pass number (0=LSB, P=MSB)
2257 for (uint32_t j = 0; j < P; j++) {
2258 // Pointer to this bucket.
2259 const uint32_t *h = &histogram[j * 256];
2260 const uint8_t *inputBytes = (const uint8_t *)input; // @@ Is this aliasing legal?
2261 inputBytes += j;
2262 if (h[inputBytes[0]] == count) {
2263 // Skip this pass, all values are the same.
2264 continue;
2265 }
2266 // Create offsets
2267 link[0] = m_ranks2;
2268 for (uint32_t i = 1; i < 256; i++) link[i] = link[i - 1] + h[i - 1];
2269 // Perform Radix Sort
2270 if (!m_validRanks) {
2271 for (uint32_t i = 0; i < count; i++) {
2272 *link[inputBytes[i * P]]++ = i;
2273 }
2274 m_validRanks = true;
2275 } else {
2276 for (uint32_t i = 0; i < count; i++) {
2277 const uint32_t idx = m_ranks[i];
2278 *link[inputBytes[idx * P]]++ = idx;
2279 }
2280 }
2281 // Swap pointers for next pass. Valid indices - the most recent ones - are in m_ranks after the swap.
2282 swap(m_ranks, m_ranks2);
2283 }
2284 // All values were equal, generate linear ranks.
2285 if (!m_validRanks) {
2286 for (uint32_t i = 0; i < count; i++)
2287 m_ranks[i] = i;
2288 m_validRanks = true;
2289 }
2290 }
2291 };
2292
2293 // Wrapping this in a class allows temporary arrays to be re-used.
2294 class BoundingBox2D
2295 {
2296 public:
2297 Vector2 majorAxis, minorAxis, minCorner, maxCorner;
2298
clear()2299 void clear()
2300 {
2301 m_boundaryVertices.clear();
2302 }
2303
appendBoundaryVertex(Vector2 v)2304 void appendBoundaryVertex(Vector2 v)
2305 {
2306 m_boundaryVertices.push_back(v);
2307 }
2308
2309 // This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method.
2310 // If vertices is null or vertexCount is 0, the boundary vertices are used.
compute(const Vector2 * vertices=nullptr,uint32_t vertexCount=0)2311 void compute(const Vector2 *vertices = nullptr, uint32_t vertexCount = 0)
2312 {
2313 if (!vertices || vertexCount == 0) {
2314 vertices = m_boundaryVertices.data();
2315 vertexCount = m_boundaryVertices.size();
2316 }
2317 convexHull(m_boundaryVertices.data(), m_boundaryVertices.size(), m_hull, 0.00001f);
2318 // @@ Ideally I should use rotating calipers to find the best box. Using brute force for now.
2319 float best_area = FLT_MAX;
2320 Vector2 best_min(0);
2321 Vector2 best_max(0);
2322 Vector2 best_axis(0);
2323 const uint32_t hullCount = m_hull.size();
2324 for (uint32_t i = 0, j = hullCount - 1; i < hullCount; j = i, i++) {
2325 if (equal(m_hull[i], m_hull[j], kEpsilon))
2326 continue;
2327 Vector2 axis = normalize(m_hull[i] - m_hull[j], 0.0f);
2328 XA_DEBUG_ASSERT(isFinite(axis));
2329 // Compute bounding box.
2330 Vector2 box_min(FLT_MAX, FLT_MAX);
2331 Vector2 box_max(-FLT_MAX, -FLT_MAX);
2332 // Consider all points, not only boundary points, in case the input chart is malformed.
2333 for (uint32_t v = 0; v < vertexCount; v++) {
2334 const Vector2 &point = vertices[v];
2335 const float x = dot(axis, point);
2336 const float y = dot(Vector2(-axis.y, axis.x), point);
2337 box_min.x = min(box_min.x, x);
2338 box_max.x = max(box_max.x, x);
2339 box_min.y = min(box_min.y, y);
2340 box_max.y = max(box_max.y, y);
2341 }
2342 // Compute box area.
2343 const float area = (box_max.x - box_min.x) * (box_max.y - box_min.y);
2344 if (area < best_area) {
2345 best_area = area;
2346 best_min = box_min;
2347 best_max = box_max;
2348 best_axis = axis;
2349 }
2350 }
2351 majorAxis = best_axis;
2352 minorAxis = Vector2(-best_axis.y, best_axis.x);
2353 minCorner = best_min;
2354 maxCorner = best_max;
2355 XA_ASSERT(isFinite(majorAxis) && isFinite(minorAxis) && isFinite(minCorner));
2356 }
2357
2358 private:
2359 // Compute the convex hull using Graham Scan.
convexHull(const Vector2 * input,uint32_t inputCount,Array<Vector2> & output,float epsilon)2360 void convexHull(const Vector2 *input, uint32_t inputCount, Array<Vector2> &output, float epsilon)
2361 {
2362 m_coords.resize(inputCount);
2363 for (uint32_t i = 0; i < inputCount; i++)
2364 m_coords[i] = input[i].x;
2365 m_radix.sort(m_coords);
2366 const uint32_t *ranks = m_radix.ranks();
2367 m_top.clear();
2368 m_bottom.clear();
2369 m_top.reserve(inputCount);
2370 m_bottom.reserve(inputCount);
2371 Vector2 P = input[ranks[0]];
2372 Vector2 Q = input[ranks[inputCount - 1]];
2373 float topy = max(P.y, Q.y);
2374 float boty = min(P.y, Q.y);
2375 for (uint32_t i = 0; i < inputCount; i++) {
2376 Vector2 p = input[ranks[i]];
2377 if (p.y >= boty)
2378 m_top.push_back(p);
2379 }
2380 for (uint32_t i = 0; i < inputCount; i++) {
2381 Vector2 p = input[ranks[inputCount - 1 - i]];
2382 if (p.y <= topy)
2383 m_bottom.push_back(p);
2384 }
2385 // Filter top list.
2386 output.clear();
2387 XA_DEBUG_ASSERT(m_top.size() >= 2);
2388 output.push_back(m_top[0]);
2389 output.push_back(m_top[1]);
2390 for (uint32_t i = 2; i < m_top.size(); ) {
2391 Vector2 a = output[output.size() - 2];
2392 Vector2 b = output[output.size() - 1];
2393 Vector2 c = m_top[i];
2394 float area = triangleArea(a, b, c);
2395 if (area >= -epsilon)
2396 output.pop_back();
2397 if (area < -epsilon || output.size() == 1) {
2398 output.push_back(c);
2399 i++;
2400 }
2401 }
2402 uint32_t top_count = output.size();
2403 XA_DEBUG_ASSERT(m_bottom.size() >= 2);
2404 output.push_back(m_bottom[1]);
2405 // Filter bottom list.
2406 for (uint32_t i = 2; i < m_bottom.size(); ) {
2407 Vector2 a = output[output.size() - 2];
2408 Vector2 b = output[output.size() - 1];
2409 Vector2 c = m_bottom[i];
2410 float area = triangleArea(a, b, c);
2411 if (area >= -epsilon)
2412 output.pop_back();
2413 if (area < -epsilon || output.size() == top_count) {
2414 output.push_back(c);
2415 i++;
2416 }
2417 }
2418 // Remove duplicate element.
2419 XA_DEBUG_ASSERT(output.size() > 0);
2420 output.pop_back();
2421 }
2422
2423 Array<Vector2> m_boundaryVertices;
2424 Array<float> m_coords;
2425 Array<Vector2> m_top, m_bottom, m_hull;
2426 RadixSort m_radix;
2427 };
2428
meshEdgeFace(uint32_t edge)2429 static uint32_t meshEdgeFace(uint32_t edge) { return edge / 3; }
meshEdgeIndex0(uint32_t edge)2430 static uint32_t meshEdgeIndex0(uint32_t edge) { return edge; }
2431
meshEdgeIndex1(uint32_t edge)2432 static uint32_t meshEdgeIndex1(uint32_t edge)
2433 {
2434 const uint32_t faceFirstEdge = edge / 3 * 3;
2435 return faceFirstEdge + (edge - faceFirstEdge + 1) % 3;
2436 }
2437
2438 struct MeshFlags
2439 {
2440 enum
2441 {
2442 HasIgnoredFaces = 1<<0,
2443 HasNormals = 1<<1
2444 };
2445 };
2446
2447 class Mesh;
2448 static void meshGetBoundaryLoops(const Mesh &mesh, Array<uint32_t> &boundaryLoops);
2449
2450 class Mesh
2451 {
2452 public:
Mesh(float epsilon,uint32_t approxVertexCount,uint32_t approxFaceCount,uint32_t flags=0,uint32_t id=UINT32_MAX)2453 Mesh(float epsilon, uint32_t approxVertexCount, uint32_t approxFaceCount, uint32_t flags = 0, uint32_t id = UINT32_MAX) : m_epsilon(epsilon), m_flags(flags), m_id(id), m_faceIgnore(MemTag::Mesh), m_indices(MemTag::MeshIndices), m_positions(MemTag::MeshPositions), m_normals(MemTag::MeshNormals), m_texcoords(MemTag::MeshTexcoords), m_nextColocalVertex(MemTag::MeshColocals), m_boundaryEdges(MemTag::MeshBoundaries), m_oppositeEdges(MemTag::MeshBoundaries), m_nextBoundaryEdges(MemTag::MeshBoundaries), m_edgeMap(MemTag::MeshEdgeMap, approxFaceCount * 3)
2454 {
2455 m_indices.reserve(approxFaceCount * 3);
2456 m_positions.reserve(approxVertexCount);
2457 m_texcoords.reserve(approxVertexCount);
2458 if (m_flags & MeshFlags::HasIgnoredFaces)
2459 m_faceIgnore.reserve(approxFaceCount);
2460 if (m_flags & MeshFlags::HasNormals)
2461 m_normals.reserve(approxVertexCount);
2462 }
2463
flags() const2464 uint32_t flags() const { return m_flags; }
id() const2465 uint32_t id() const { return m_id; }
2466
addVertex(const Vector3 & pos,const Vector3 & normal=Vector3 (0.0f),const Vector2 & texcoord=Vector2 (0.0f))2467 void addVertex(const Vector3 &pos, const Vector3 &normal = Vector3(0.0f), const Vector2 &texcoord = Vector2(0.0f))
2468 {
2469 XA_DEBUG_ASSERT(isFinite(pos));
2470 m_positions.push_back(pos);
2471 if (m_flags & MeshFlags::HasNormals)
2472 m_normals.push_back(normal);
2473 m_texcoords.push_back(texcoord);
2474 }
2475
2476 struct AddFaceResult
2477 {
2478 enum Enum
2479 {
2480 OK,
2481 DuplicateEdge = 1
2482 };
2483 };
2484
addFace(uint32_t v0,uint32_t v1,uint32_t v2,bool ignore=false)2485 AddFaceResult::Enum addFace(uint32_t v0, uint32_t v1, uint32_t v2, bool ignore = false)
2486 {
2487 uint32_t indexArray[3];
2488 indexArray[0] = v0;
2489 indexArray[1] = v1;
2490 indexArray[2] = v2;
2491 return addFace(indexArray, ignore);
2492 }
2493
addFace(const uint32_t * indices,bool ignore=false)2494 AddFaceResult::Enum addFace(const uint32_t *indices, bool ignore = false)
2495 {
2496 AddFaceResult::Enum result = AddFaceResult::OK;
2497 if (m_flags & MeshFlags::HasIgnoredFaces)
2498 m_faceIgnore.push_back(ignore);
2499 const uint32_t firstIndex = m_indices.size();
2500 for (uint32_t i = 0; i < 3; i++)
2501 m_indices.push_back(indices[i]);
2502 for (uint32_t i = 0; i < 3; i++) {
2503 const uint32_t vertex0 = m_indices[firstIndex + i];
2504 const uint32_t vertex1 = m_indices[firstIndex + (i + 1) % 3];
2505 const EdgeKey key(vertex0, vertex1);
2506 if (m_edgeMap.get(key) != UINT32_MAX)
2507 result = AddFaceResult::DuplicateEdge;
2508 m_edgeMap.add(key);
2509 }
2510 return result;
2511 }
2512
createColocals()2513 void createColocals()
2514 {
2515 const uint32_t vertexCount = m_positions.size();
2516 Array<AABB> aabbs(MemTag::BVH);
2517 aabbs.resize(vertexCount);
2518 for (uint32_t i = 0; i < m_positions.size(); i++)
2519 aabbs[i] = AABB(m_positions[i], m_epsilon);
2520 BVH bvh(aabbs);
2521 Array<uint32_t> colocals(MemTag::MeshColocals);
2522 Array<uint32_t> potential(MemTag::MeshColocals);
2523 m_nextColocalVertex.resize(vertexCount);
2524 m_nextColocalVertex.fillBytes(0xff);
2525 for (uint32_t i = 0; i < vertexCount; i++) {
2526 if (m_nextColocalVertex[i] != UINT32_MAX)
2527 continue; // Already linked.
2528 // Find other vertices colocal to this one.
2529 colocals.clear();
2530 colocals.push_back(i); // Always add this vertex.
2531 bvh.query(AABB(m_positions[i], m_epsilon), potential);
2532 for (uint32_t j = 0; j < potential.size(); j++) {
2533 const uint32_t otherVertex = potential[j];
2534 if (otherVertex != i && equal(m_positions[i], m_positions[otherVertex], m_epsilon) && m_nextColocalVertex[otherVertex] == UINT32_MAX)
2535 colocals.push_back(otherVertex);
2536 }
2537 if (colocals.size() == 1) {
2538 // No colocals for this vertex.
2539 m_nextColocalVertex[i] = i;
2540 continue;
2541 }
2542 // Link in ascending order.
2543 insertionSort(colocals.data(), colocals.size());
2544 for (uint32_t j = 0; j < colocals.size(); j++)
2545 m_nextColocalVertex[colocals[j]] = colocals[(j + 1) % colocals.size()];
2546 XA_DEBUG_ASSERT(m_nextColocalVertex[i] != UINT32_MAX);
2547 }
2548 }
2549
createBoundaries()2550 void createBoundaries()
2551 {
2552 const uint32_t edgeCount = m_indices.size();
2553 const uint32_t vertexCount = m_positions.size();
2554 m_oppositeEdges.resize(edgeCount);
2555 m_boundaryEdges.reserve(uint32_t(edgeCount * 0.1f));
2556 m_isBoundaryVertex.resize(vertexCount);
2557 m_isBoundaryVertex.zeroOutMemory();
2558 for (uint32_t i = 0; i < edgeCount; i++)
2559 m_oppositeEdges[i] = UINT32_MAX;
2560 const uint32_t faceCount = m_indices.size() / 3;
2561 for (uint32_t i = 0; i < faceCount; i++) {
2562 if (isFaceIgnored(i))
2563 continue;
2564 for (uint32_t j = 0; j < 3; j++) {
2565 const uint32_t edge = i * 3 + j;
2566 const uint32_t vertex0 = m_indices[edge];
2567 const uint32_t vertex1 = m_indices[i * 3 + (j + 1) % 3];
2568 // If there is an edge with opposite winding to this one, the edge isn't on a boundary.
2569 const uint32_t oppositeEdge = findEdge(vertex1, vertex0);
2570 if (oppositeEdge != UINT32_MAX) {
2571 m_oppositeEdges[edge] = oppositeEdge;
2572 } else {
2573 m_boundaryEdges.push_back(edge);
2574 m_isBoundaryVertex.set(vertex0);
2575 m_isBoundaryVertex.set(vertex1);
2576 }
2577 }
2578 }
2579 }
2580
linkBoundaries()2581 void linkBoundaries()
2582 {
2583 const uint32_t edgeCount = m_indices.size();
2584 HashMap<uint32_t> vertexToEdgeMap(MemTag::Mesh, edgeCount); // Edge is index / 2
2585 for (uint32_t i = 0; i < edgeCount; i++) {
2586 vertexToEdgeMap.add(m_indices[meshEdgeIndex0(i)]);
2587 vertexToEdgeMap.add(m_indices[meshEdgeIndex1(i)]);
2588 }
2589 m_nextBoundaryEdges.resize(edgeCount);
2590 for (uint32_t i = 0; i < edgeCount; i++)
2591 m_nextBoundaryEdges[i] = UINT32_MAX;
2592 uint32_t numBoundaryLoops = 0, numUnclosedBoundaries = 0;
2593 BitArray linkedEdges(edgeCount);
2594 linkedEdges.zeroOutMemory();
2595 for (;;) {
2596 // Find the first boundary edge that hasn't been linked yet.
2597 uint32_t firstEdge = UINT32_MAX;
2598 for (uint32_t i = 0; i < edgeCount; i++) {
2599 if (m_oppositeEdges[i] == UINT32_MAX && !linkedEdges.get(i)) {
2600 firstEdge = i;
2601 break;
2602 }
2603 }
2604 if (firstEdge == UINT32_MAX)
2605 break;
2606 uint32_t currentEdge = firstEdge;
2607 for (;;) {
2608 // Find the next boundary edge. The first vertex will be the same as (or colocal to) the current edge second vertex.
2609 const uint32_t startVertex = m_indices[meshEdgeIndex1(currentEdge)];
2610 uint32_t bestNextEdge = UINT32_MAX;
2611 for (ColocalVertexIterator it(this, startVertex); !it.isDone(); it.advance()) {
2612 uint32_t mapIndex = vertexToEdgeMap.get(it.vertex());
2613 while (mapIndex != UINT32_MAX) {
2614 const uint32_t otherEdge = mapIndex / 2; // Two vertices added per edge.
2615 if (m_oppositeEdges[otherEdge] != UINT32_MAX)
2616 goto next; // Not a boundary edge.
2617 if (linkedEdges.get(otherEdge))
2618 goto next; // Already linked.
2619 if (m_indices[meshEdgeIndex0(otherEdge)] != it.vertex())
2620 goto next; // Edge contains the vertex, but it's the wrong one.
2621 // First edge (closing the boundary loop) has the highest priority.
2622 // Non-colocal vertex has the next highest.
2623 if (bestNextEdge != firstEdge && (bestNextEdge == UINT32_MAX || it.vertex() == startVertex))
2624 bestNextEdge = otherEdge;
2625 next:
2626 mapIndex = vertexToEdgeMap.getNext(mapIndex);
2627 }
2628 }
2629 if (bestNextEdge == UINT32_MAX) {
2630 numUnclosedBoundaries++;
2631 if (currentEdge == firstEdge)
2632 linkedEdges.set(firstEdge); // Only 1 edge in this boundary "loop".
2633 break; // Can't find a next edge.
2634 }
2635 m_nextBoundaryEdges[currentEdge] = bestNextEdge;
2636 linkedEdges.set(bestNextEdge);
2637 currentEdge = bestNextEdge;
2638 if (currentEdge == firstEdge) {
2639 numBoundaryLoops++;
2640 break; // Closed the boundary loop.
2641 }
2642 }
2643 }
2644 #if XA_FIX_INTERNAL_BOUNDARY_LOOPS
2645 // Find internal boundary loops and separate them.
2646 // Detect by finding two edges in a boundary loop that have a colocal end vertex.
2647 // Fix by swapping their next boundary edge.
2648 // Need to start over after every fix since known boundary loops have changed.
2649 Array<uint32_t> boundaryLoops;
2650 fixInternalBoundary:
2651 meshGetBoundaryLoops(*this, boundaryLoops);
2652 for (uint32_t loop = 0; loop < boundaryLoops.size(); loop++) {
2653 linkedEdges.zeroOutMemory();
2654 for (Mesh::BoundaryLoopEdgeIterator it1(this, boundaryLoops[loop]); !it1.isDone(); it1.advance()) {
2655 const uint32_t e1 = it1.edge();
2656 if (linkedEdges.get(e1))
2657 continue;
2658 for (Mesh::BoundaryLoopEdgeIterator it2(this, boundaryLoops[loop]); !it2.isDone(); it2.advance()) {
2659 const uint32_t e2 = it2.edge();
2660 if (e1 == e2 || !isBoundaryEdge(e2) || linkedEdges.get(e2))
2661 continue;
2662 if (!areColocal(m_indices[meshEdgeIndex1(e1)], m_indices[meshEdgeIndex1(e2)]))
2663 continue;
2664 swap(m_nextBoundaryEdges[e1], m_nextBoundaryEdges[e2]);
2665 linkedEdges.set(e1);
2666 linkedEdges.set(e2);
2667 goto fixInternalBoundary; // start over
2668 }
2669 }
2670 }
2671 #endif
2672 }
2673
2674 /// Find edge, test all colocals.
findEdge(uint32_t vertex0,uint32_t vertex1) const2675 uint32_t findEdge(uint32_t vertex0, uint32_t vertex1) const
2676 {
2677 uint32_t result = UINT32_MAX;
2678 if (m_nextColocalVertex.isEmpty()) {
2679 EdgeKey key(vertex0, vertex1);
2680 uint32_t edge = m_edgeMap.get(key);
2681 while (edge != UINT32_MAX) {
2682 // Don't find edges of ignored faces.
2683 if (!isFaceIgnored(meshEdgeFace(edge))) {
2684 //XA_DEBUG_ASSERT(m_id != UINT32_MAX || (m_id == UINT32_MAX && result == UINT32_MAX)); // duplicate edge - ignore on initial meshes
2685 result = edge;
2686 #if !XA_DEBUG
2687 return result;
2688 #endif
2689 }
2690 edge = m_edgeMap.getNext(edge);
2691 }
2692 } else {
2693 for (ColocalVertexIterator it0(this, vertex0); !it0.isDone(); it0.advance()) {
2694 for (ColocalVertexIterator it1(this, vertex1); !it1.isDone(); it1.advance()) {
2695 EdgeKey key(it0.vertex(), it1.vertex());
2696 uint32_t edge = m_edgeMap.get(key);
2697 while (edge != UINT32_MAX) {
2698 // Don't find edges of ignored faces.
2699 if (!isFaceIgnored(meshEdgeFace(edge))) {
2700 XA_DEBUG_ASSERT(m_id != UINT32_MAX || (m_id == UINT32_MAX && result == UINT32_MAX)); // duplicate edge - ignore on initial meshes
2701 result = edge;
2702 #if !XA_DEBUG
2703 return result;
2704 #endif
2705 }
2706 edge = m_edgeMap.getNext(edge);
2707 }
2708 }
2709 }
2710 }
2711 return result;
2712 }
2713
2714 // Edge map can be destroyed when no longer used to reduce memory usage. It's used by:
2715 // * Mesh::createBoundaries()
2716 // * Mesh::ColocalEdgeIterator (used by MeshFaceGroups)
2717 // * meshCloseHole()
destroyEdgeMap()2718 void destroyEdgeMap()
2719 {
2720 m_edgeMap.destroy();
2721 }
2722
2723 #if XA_DEBUG_EXPORT_OBJ
writeObjVertices(FILE * file) const2724 void writeObjVertices(FILE *file) const
2725 {
2726 for (uint32_t i = 0; i < m_positions.size(); i++)
2727 fprintf(file, "v %g %g %g\n", m_positions[i].x, m_positions[i].y, m_positions[i].z);
2728 if (m_flags & MeshFlags::HasNormals) {
2729 for (uint32_t i = 0; i < m_normals.size(); i++)
2730 fprintf(file, "vn %g %g %g\n", m_normals[i].x, m_normals[i].y, m_normals[i].z);
2731 }
2732 for (uint32_t i = 0; i < m_texcoords.size(); i++)
2733 fprintf(file, "vt %g %g\n", m_texcoords[i].x, m_texcoords[i].y);
2734 }
2735
writeObjFace(FILE * file,uint32_t face,uint32_t offset=0) const2736 void writeObjFace(FILE *file, uint32_t face, uint32_t offset = 0) const
2737 {
2738 fprintf(file, "f ");
2739 for (uint32_t j = 0; j < 3; j++) {
2740 const uint32_t index = m_indices[face * 3 + j] + 1 + offset; // 1-indexed
2741 fprintf(file, "%d/%d/%d%c", index, index, index, j == 2 ? '\n' : ' ');
2742 }
2743 }
2744
writeObjBoundaryEges(FILE * file) const2745 void writeObjBoundaryEges(FILE *file) const
2746 {
2747 if (m_oppositeEdges.isEmpty())
2748 return; // Boundaries haven't been created.
2749 fprintf(file, "o boundary_edges\n");
2750 for (uint32_t i = 0; i < edgeCount(); i++) {
2751 if (m_oppositeEdges[i] != UINT32_MAX)
2752 continue;
2753 fprintf(file, "l %d %d\n", m_indices[meshEdgeIndex0(i)] + 1, m_indices[meshEdgeIndex1(i)] + 1); // 1-indexed
2754 }
2755 }
2756
writeObjLinkedBoundaries(FILE * file) const2757 void writeObjLinkedBoundaries(FILE *file) const
2758 {
2759 if (m_oppositeEdges.isEmpty() || m_nextBoundaryEdges.isEmpty())
2760 return; // Boundaries haven't been created and/or linked.
2761 Array<uint32_t> boundaryLoops;
2762 meshGetBoundaryLoops(*this, boundaryLoops);
2763 for (uint32_t i = 0; i < boundaryLoops.size(); i++) {
2764 uint32_t edge = boundaryLoops[i];
2765 fprintf(file, "o boundary_%04d\n", i);
2766 fprintf(file, "l");
2767 for (;;) {
2768 const uint32_t vertex0 = m_indices[meshEdgeIndex0(edge)];
2769 const uint32_t vertex1 = m_indices[meshEdgeIndex1(edge)];
2770 fprintf(file, " %d", vertex0 + 1); // 1-indexed
2771 edge = m_nextBoundaryEdges[edge];
2772 if (edge == boundaryLoops[i] || edge == UINT32_MAX) {
2773 fprintf(file, " %d\n", vertex1 + 1); // 1-indexed
2774 break;
2775 }
2776 }
2777 }
2778 }
2779
writeObjFile(const char * filename) const2780 void writeObjFile(const char *filename) const
2781 {
2782 FILE *file;
2783 XA_FOPEN(file, filename, "w");
2784 if (!file)
2785 return;
2786 writeObjVertices(file);
2787 fprintf(file, "s off\n");
2788 fprintf(file, "o object\n");
2789 for (uint32_t i = 0; i < faceCount(); i++)
2790 writeObjFace(file, i);
2791 writeObjBoundaryEges(file);
2792 writeObjLinkedBoundaries(file);
2793 fclose(file);
2794 }
2795 #endif
2796
computeSurfaceArea() const2797 float computeSurfaceArea() const
2798 {
2799 float area = 0;
2800 for (uint32_t f = 0; f < faceCount(); f++)
2801 area += computeFaceArea(f);
2802 XA_DEBUG_ASSERT(area >= 0);
2803 return area;
2804 }
2805
2806 // Returned value is always positive, even if some triangles are flipped.
computeParametricArea() const2807 float computeParametricArea() const
2808 {
2809 float area = 0;
2810 for (uint32_t f = 0; f < faceCount(); f++)
2811 area += fabsf(computeFaceParametricArea(f)); // May be negative, depends on texcoord winding.
2812 return area;
2813 }
2814
computeFaceArea(uint32_t face) const2815 float computeFaceArea(uint32_t face) const
2816 {
2817 const Vector3 &p0 = m_positions[m_indices[face * 3 + 0]];
2818 const Vector3 &p1 = m_positions[m_indices[face * 3 + 1]];
2819 const Vector3 &p2 = m_positions[m_indices[face * 3 + 2]];
2820 return length(cross(p1 - p0, p2 - p0)) * 0.5f;
2821 }
2822
computeFaceCentroid(uint32_t face) const2823 Vector3 computeFaceCentroid(uint32_t face) const
2824 {
2825 Vector3 sum(0.0f);
2826 for (uint32_t i = 0; i < 3; i++)
2827 sum += m_positions[m_indices[face * 3 + i]];
2828 return sum / 3.0f;
2829 }
2830
2831 // Average of the edge midpoints weighted by the edge length.
2832 // I want a point inside the triangle, but closer to the cirumcenter.
computeFaceCenter(uint32_t face) const2833 Vector3 computeFaceCenter(uint32_t face) const
2834 {
2835 const Vector3 &p0 = m_positions[m_indices[face * 3 + 0]];
2836 const Vector3 &p1 = m_positions[m_indices[face * 3 + 1]];
2837 const Vector3 &p2 = m_positions[m_indices[face * 3 + 2]];
2838 const float l0 = length(p1 - p0);
2839 const float l1 = length(p2 - p1);
2840 const float l2 = length(p0 - p2);
2841 const Vector3 m0 = (p0 + p1) * l0 / (l0 + l1 + l2);
2842 const Vector3 m1 = (p1 + p2) * l1 / (l0 + l1 + l2);
2843 const Vector3 m2 = (p2 + p0) * l2 / (l0 + l1 + l2);
2844 return m0 + m1 + m2;
2845 }
2846
computeFaceNormal(uint32_t face) const2847 Vector3 computeFaceNormal(uint32_t face) const
2848 {
2849 const Vector3 &p0 = m_positions[m_indices[face * 3 + 0]];
2850 const Vector3 &p1 = m_positions[m_indices[face * 3 + 1]];
2851 const Vector3 &p2 = m_positions[m_indices[face * 3 + 2]];
2852 const Vector3 e0 = p2 - p0;
2853 const Vector3 e1 = p1 - p0;
2854 const Vector3 normalAreaScaled = cross(e0, e1);
2855 return normalizeSafe(normalAreaScaled, Vector3(0, 0, 1), 0.0f);
2856 }
2857
computeFaceParametricArea(uint32_t face) const2858 float computeFaceParametricArea(uint32_t face) const
2859 {
2860 const Vector2 &t0 = m_texcoords[m_indices[face * 3 + 0]];
2861 const Vector2 &t1 = m_texcoords[m_indices[face * 3 + 1]];
2862 const Vector2 &t2 = m_texcoords[m_indices[face * 3 + 2]];
2863 return triangleArea(t0, t1, t2);
2864 }
2865
2866 // @@ This is not exactly accurate, we should compare the texture coordinates...
isSeam(uint32_t edge) const2867 bool isSeam(uint32_t edge) const
2868 {
2869 const uint32_t oppositeEdge = m_oppositeEdges[edge];
2870 if (oppositeEdge == UINT32_MAX)
2871 return false; // boundary edge
2872 const uint32_t e0 = meshEdgeIndex0(edge);
2873 const uint32_t e1 = meshEdgeIndex1(edge);
2874 const uint32_t oe0 = meshEdgeIndex0(oppositeEdge);
2875 const uint32_t oe1 = meshEdgeIndex1(oppositeEdge);
2876 return m_indices[e0] != m_indices[oe1] || m_indices[e1] != m_indices[oe0];
2877 }
2878
isTextureSeam(uint32_t edge) const2879 bool isTextureSeam(uint32_t edge) const
2880 {
2881 const uint32_t oppositeEdge = m_oppositeEdges[edge];
2882 if (oppositeEdge == UINT32_MAX)
2883 return false; // boundary edge
2884 const uint32_t e0 = meshEdgeIndex0(edge);
2885 const uint32_t e1 = meshEdgeIndex1(edge);
2886 const uint32_t oe0 = meshEdgeIndex0(oppositeEdge);
2887 const uint32_t oe1 = meshEdgeIndex1(oppositeEdge);
2888 return m_texcoords[m_indices[e0]] != m_texcoords[m_indices[oe1]] || m_texcoords[m_indices[e1]] != m_texcoords[m_indices[oe0]];
2889 }
2890
firstColocal(uint32_t vertex) const2891 uint32_t firstColocal(uint32_t vertex) const
2892 {
2893 for (ColocalVertexIterator it(this, vertex); !it.isDone(); it.advance()) {
2894 if (it.vertex() < vertex)
2895 vertex = it.vertex();
2896 }
2897 return vertex;
2898 }
2899
areColocal(uint32_t vertex0,uint32_t vertex1) const2900 bool areColocal(uint32_t vertex0, uint32_t vertex1) const
2901 {
2902 if (vertex0 == vertex1)
2903 return true;
2904 if (m_nextColocalVertex.isEmpty())
2905 return false;
2906 for (ColocalVertexIterator it(this, vertex0); !it.isDone(); it.advance()) {
2907 if (it.vertex() == vertex1)
2908 return true;
2909 }
2910 return false;
2911 }
2912
epsilon() const2913 XA_INLINE float epsilon() const { return m_epsilon; }
edgeCount() const2914 XA_INLINE uint32_t edgeCount() const { return m_indices.size(); }
oppositeEdge(uint32_t edge) const2915 XA_INLINE uint32_t oppositeEdge(uint32_t edge) const { return m_oppositeEdges[edge]; }
isBoundaryEdge(uint32_t edge) const2916 XA_INLINE bool isBoundaryEdge(uint32_t edge) const { return m_oppositeEdges[edge] == UINT32_MAX; }
boundaryEdges() const2917 XA_INLINE const Array<uint32_t> &boundaryEdges() const { return m_boundaryEdges; }
isBoundaryVertex(uint32_t vertex) const2918 XA_INLINE bool isBoundaryVertex(uint32_t vertex) const { return m_isBoundaryVertex.get(vertex); }
vertexCount() const2919 XA_INLINE uint32_t vertexCount() const { return m_positions.size(); }
vertexAt(uint32_t i) const2920 XA_INLINE uint32_t vertexAt(uint32_t i) const { return m_indices[i]; }
position(uint32_t vertex) const2921 XA_INLINE const Vector3 &position(uint32_t vertex) const { return m_positions[vertex]; }
positions() const2922 XA_INLINE const Vector3 *positions() const { return m_positions.data(); }
normal(uint32_t vertex) const2923 XA_INLINE const Vector3 &normal(uint32_t vertex) const { XA_DEBUG_ASSERT(m_flags & MeshFlags::HasNormals); return m_normals[vertex]; }
texcoord(uint32_t vertex) const2924 XA_INLINE const Vector2 &texcoord(uint32_t vertex) const { return m_texcoords[vertex]; }
texcoord(uint32_t vertex)2925 XA_INLINE Vector2 &texcoord(uint32_t vertex) { return m_texcoords[vertex]; }
texcoords() const2926 XA_INLINE const Vector2 *texcoords() const { return m_texcoords.data(); }
texcoords()2927 XA_INLINE Vector2 *texcoords() { return m_texcoords.data(); }
faceCount() const2928 XA_INLINE uint32_t faceCount() const { return m_indices.size() / 3; }
indices() const2929 XA_INLINE const uint32_t *indices() const { return m_indices.data(); }
indexCount() const2930 XA_INLINE uint32_t indexCount() const { return m_indices.size(); }
isFaceIgnored(uint32_t face) const2931 XA_INLINE bool isFaceIgnored(uint32_t face) const { return (m_flags & MeshFlags::HasIgnoredFaces) && m_faceIgnore[face]; }
2932
2933 private:
2934
2935 float m_epsilon;
2936 uint32_t m_flags;
2937 uint32_t m_id;
2938 Array<bool> m_faceIgnore;
2939 Array<uint32_t> m_indices;
2940 Array<Vector3> m_positions;
2941 Array<Vector3> m_normals;
2942 Array<Vector2> m_texcoords;
2943
2944 // Populated by createColocals
2945 Array<uint32_t> m_nextColocalVertex; // In: vertex index. Out: the vertex index of the next colocal position.
2946
2947 // Populated by createBoundaries
2948 BitArray m_isBoundaryVertex;
2949 Array<uint32_t> m_boundaryEdges;
2950 Array<uint32_t> m_oppositeEdges; // In: edge index. Out: the index of the opposite edge (i.e. wound the opposite direction). UINT32_MAX if the input edge is a boundary edge.
2951
2952 // Populated by linkBoundaries
2953 Array<uint32_t> m_nextBoundaryEdges; // The index of the next boundary edge. UINT32_MAX if the edge is not a boundary edge.
2954
2955 struct EdgeKey
2956 {
EdgeKeyxatlas::internal::Mesh::EdgeKey2957 EdgeKey(const EdgeKey &k) : v0(k.v0), v1(k.v1) {}
EdgeKeyxatlas::internal::Mesh::EdgeKey2958 EdgeKey(uint32_t v0, uint32_t v1) : v0(v0), v1(v1) {}
operator ==xatlas::internal::Mesh::EdgeKey2959 bool operator==(const EdgeKey &k) const { return v0 == k.v0 && v1 == k.v1; }
2960
2961 uint32_t v0;
2962 uint32_t v1;
2963 };
2964
2965 struct EdgeHash
2966 {
operator ()xatlas::internal::Mesh::EdgeHash2967 uint32_t operator()(const EdgeKey &k) const { return k.v0 * 32768u + k.v1; }
2968 };
2969
2970 HashMap<EdgeKey, EdgeHash> m_edgeMap;
2971
2972 public:
2973 class BoundaryLoopEdgeIterator
2974 {
2975 public:
BoundaryLoopEdgeIterator(const Mesh * mesh,uint32_t edge)2976 BoundaryLoopEdgeIterator(const Mesh *mesh, uint32_t edge) : m_mesh(mesh), m_first(UINT32_MAX), m_current(edge) {}
2977
advance()2978 void advance()
2979 {
2980 if (m_first == UINT32_MAX)
2981 m_first = m_current;
2982 m_current = m_mesh->m_nextBoundaryEdges[m_current];
2983 }
2984
isDone() const2985 bool isDone() const
2986 {
2987 return m_first == m_current || m_current == UINT32_MAX;
2988 }
2989
edge() const2990 uint32_t edge() const
2991 {
2992 return m_current;
2993 }
2994
nextEdge() const2995 uint32_t nextEdge() const
2996 {
2997 return m_mesh->m_nextBoundaryEdges[m_current];
2998 }
2999
3000 private:
3001 const Mesh *m_mesh;
3002 uint32_t m_first;
3003 uint32_t m_current;
3004 };
3005
3006 class ColocalVertexIterator
3007 {
3008 public:
ColocalVertexIterator(const Mesh * mesh,uint32_t v)3009 ColocalVertexIterator(const Mesh *mesh, uint32_t v) : m_mesh(mesh), m_first(UINT32_MAX), m_current(v) {}
3010
advance()3011 void advance()
3012 {
3013 if (m_first == UINT32_MAX)
3014 m_first = m_current;
3015 if (!m_mesh->m_nextColocalVertex.isEmpty())
3016 m_current = m_mesh->m_nextColocalVertex[m_current];
3017 }
3018
isDone() const3019 bool isDone() const
3020 {
3021 return m_first == m_current;
3022 }
3023
vertex() const3024 uint32_t vertex() const
3025 {
3026 return m_current;
3027 }
3028
pos() const3029 const Vector3 *pos() const
3030 {
3031 return &m_mesh->m_positions[m_current];
3032 }
3033
3034 private:
3035 const Mesh *m_mesh;
3036 uint32_t m_first;
3037 uint32_t m_current;
3038 };
3039
3040 class ColocalEdgeIterator
3041 {
3042 public:
ColocalEdgeIterator(const Mesh * mesh,uint32_t vertex0,uint32_t vertex1)3043 ColocalEdgeIterator(const Mesh *mesh, uint32_t vertex0, uint32_t vertex1) : m_mesh(mesh), m_vertex0It(mesh, vertex0), m_vertex1It(mesh, vertex1), m_vertex1(vertex1)
3044 {
3045 do {
3046 if (!resetElement()) {
3047 advanceVertex1();
3048 }
3049 else {
3050 break;
3051 }
3052 } while (!isDone());
3053 }
3054
advance()3055 void advance()
3056 {
3057 advanceElement();
3058 }
3059
isDone() const3060 bool isDone() const
3061 {
3062 return m_vertex0It.isDone() && m_vertex1It.isDone() && m_edge == UINT32_MAX;
3063 }
3064
edge() const3065 uint32_t edge() const
3066 {
3067 return m_edge;
3068 }
3069
3070 private:
resetElement()3071 bool resetElement()
3072 {
3073 m_edge = m_mesh->m_edgeMap.get(Mesh::EdgeKey(m_vertex0It.vertex(), m_vertex1It.vertex()));
3074 while (m_edge != UINT32_MAX) {
3075 if (!isIgnoredFace())
3076 break;
3077 m_edge = m_mesh->m_edgeMap.getNext(m_edge);
3078 }
3079 if (m_edge == UINT32_MAX) {
3080 return false;
3081 }
3082 return true;
3083 }
3084
advanceElement()3085 void advanceElement()
3086 {
3087 for (;;) {
3088 m_edge = m_mesh->m_edgeMap.getNext(m_edge);
3089 if (m_edge == UINT32_MAX)
3090 break;
3091 if (!isIgnoredFace())
3092 break;
3093 }
3094 if (m_edge == UINT32_MAX)
3095 advanceVertex1();
3096 }
3097
advanceVertex1()3098 void advanceVertex1()
3099 {
3100 auto successful = false;
3101 while (!successful) {
3102 m_vertex1It.advance();
3103 if (m_vertex1It.isDone()) {
3104 if (!m_vertex0It.isDone()) {
3105 m_vertex0It.advance();
3106 m_vertex1It = ColocalVertexIterator(m_mesh, m_vertex1);
3107 }
3108 else {
3109 return;
3110 }
3111 }
3112 successful = resetElement();
3113 }
3114 }
3115
isIgnoredFace() const3116 bool isIgnoredFace() const
3117 {
3118 return m_mesh->m_faceIgnore[meshEdgeFace(m_edge)];
3119 }
3120
3121 const Mesh *m_mesh;
3122 ColocalVertexIterator m_vertex0It, m_vertex1It;
3123 const uint32_t m_vertex1;
3124 uint32_t m_edge;
3125 };
3126
3127 class FaceEdgeIterator
3128 {
3129 public:
FaceEdgeIterator(const Mesh * mesh,uint32_t face)3130 FaceEdgeIterator (const Mesh *mesh, uint32_t face) : m_mesh(mesh), m_face(face), m_relativeEdge(0)
3131 {
3132 m_edge = m_face * 3;
3133 }
3134
advance()3135 void advance()
3136 {
3137 if (m_relativeEdge < 3) {
3138 m_edge++;
3139 m_relativeEdge++;
3140 }
3141 }
3142
isDone() const3143 bool isDone() const
3144 {
3145 return m_relativeEdge == 3;
3146 }
3147
isBoundary() const3148 bool isBoundary() const { return m_mesh->m_oppositeEdges[m_edge] == UINT32_MAX; }
isSeam() const3149 bool isSeam() const { return m_mesh->isSeam(m_edge); }
isTextureSeam() const3150 bool isTextureSeam() const { return m_mesh->isTextureSeam(m_edge); }
edge() const3151 uint32_t edge() const { return m_edge; }
relativeEdge() const3152 uint32_t relativeEdge() const { return m_relativeEdge; }
face() const3153 uint32_t face() const { return m_face; }
oppositeEdge() const3154 uint32_t oppositeEdge() const { return m_mesh->m_oppositeEdges[m_edge]; }
3155
oppositeFace() const3156 uint32_t oppositeFace() const
3157 {
3158 const uint32_t oedge = m_mesh->m_oppositeEdges[m_edge];
3159 if (oedge == UINT32_MAX)
3160 return UINT32_MAX;
3161 return meshEdgeFace(oedge);
3162 }
3163
vertex0() const3164 uint32_t vertex0() const { return m_mesh->m_indices[m_face * 3 + m_relativeEdge]; }
vertex1() const3165 uint32_t vertex1() const { return m_mesh->m_indices[m_face * 3 + (m_relativeEdge + 1) % 3]; }
position0() const3166 const Vector3 &position0() const { return m_mesh->m_positions[vertex0()]; }
position1() const3167 const Vector3 &position1() const { return m_mesh->m_positions[vertex1()]; }
normal0() const3168 const Vector3 &normal0() const { return m_mesh->m_normals[vertex0()]; }
normal1() const3169 const Vector3 &normal1() const { return m_mesh->m_normals[vertex1()]; }
texcoord0() const3170 const Vector2 &texcoord0() const { return m_mesh->m_texcoords[vertex0()]; }
texcoord1() const3171 const Vector2 &texcoord1() const { return m_mesh->m_texcoords[vertex1()]; }
3172
3173 private:
3174 const Mesh *m_mesh;
3175 uint32_t m_face;
3176 uint32_t m_edge;
3177 uint32_t m_relativeEdge;
3178 };
3179 };
3180
3181 struct MeshFaceGroups
3182 {
3183 typedef uint32_t Handle;
3184 static constexpr Handle kInvalid = UINT32_MAX;
3185
MeshFaceGroupsxatlas::internal::MeshFaceGroups3186 MeshFaceGroups(const Mesh *mesh) : m_mesh(mesh), m_groups(MemTag::Mesh), m_firstFace(MemTag::Mesh), m_nextFace(MemTag::Mesh), m_faceCount(MemTag::Mesh) {}
groupAtxatlas::internal::MeshFaceGroups3187 XA_INLINE Handle groupAt(uint32_t face) const { return m_groups[face]; }
groupCountxatlas::internal::MeshFaceGroups3188 XA_INLINE uint32_t groupCount() const { return m_faceCount.size(); }
nextFacexatlas::internal::MeshFaceGroups3189 XA_INLINE uint32_t nextFace(uint32_t face) const { return m_nextFace[face]; }
faceCountxatlas::internal::MeshFaceGroups3190 XA_INLINE uint32_t faceCount(uint32_t group) const { return m_faceCount[group]; }
3191
computexatlas::internal::MeshFaceGroups3192 void compute()
3193 {
3194 m_groups.resize(m_mesh->faceCount());
3195 m_groups.fillBytes(0xff); // Set all faces to kInvalid
3196 uint32_t firstUnassignedFace = 0;
3197 Handle group = 0;
3198 Array<uint32_t> growFaces;
3199 const uint32_t n = m_mesh->faceCount();
3200 m_nextFace.resize(n);
3201 for (;;) {
3202 // Find an unassigned face.
3203 uint32_t face = UINT32_MAX;
3204 for (uint32_t f = firstUnassignedFace; f < n; f++) {
3205 if (m_groups[f] == kInvalid && !m_mesh->isFaceIgnored(f)) {
3206 face = f;
3207 firstUnassignedFace = f + 1;
3208 break;
3209 }
3210 }
3211 if (face == UINT32_MAX)
3212 break; // All faces assigned to a group (except ignored faces).
3213 m_groups[face] = group;
3214 m_nextFace[face] = UINT32_MAX;
3215 m_firstFace.push_back(face);
3216 growFaces.clear();
3217 growFaces.push_back(face);
3218 uint32_t prevFace = face, groupFaceCount = 1;
3219 // Find faces connected to the face and assign them to the same group as the face, unless they are already assigned to another group.
3220 for (;;) {
3221 if (growFaces.isEmpty())
3222 break;
3223 const uint32_t f = growFaces.back();
3224 growFaces.pop_back();
3225 for (Mesh::FaceEdgeIterator edgeIt(m_mesh, f); !edgeIt.isDone(); edgeIt.advance()) {
3226 // Iterate opposite edges. There may be more than one - non-manifold geometry can have duplicate edges.
3227 // Prioritize the one with exact vertex match, not just colocal.
3228 // If *any* of the opposite edges are already assigned to this group, don't do anything.
3229 bool alreadyAssignedToThisGroup = false;
3230 uint32_t bestConnectedFace = UINT32_MAX;
3231 for (Mesh::ColocalEdgeIterator oppositeEdgeIt(m_mesh, edgeIt.vertex1(), edgeIt.vertex0()); !oppositeEdgeIt.isDone(); oppositeEdgeIt.advance()) {
3232 const uint32_t oppositeEdge = oppositeEdgeIt.edge();
3233 const uint32_t oppositeFace = meshEdgeFace(oppositeEdge);
3234 #if 0
3235 // Reject opposite face if dihedral angle >= 90 degrees.
3236 {
3237 Vector3 a = m_mesh->computeFaceNormal(f);
3238 Vector3 b = m_mesh->computeFaceNormal(oppositeFace);
3239 if (dot(a, b) <= 0.0f)
3240 continue;
3241 }
3242 #endif
3243 if (m_mesh->isFaceIgnored(oppositeFace))
3244 continue; // Don't add ignored faces to group.
3245 if (m_groups[oppositeFace] == group) {
3246 alreadyAssignedToThisGroup = true;
3247 break;
3248 }
3249 if (m_groups[oppositeFace] != kInvalid)
3250 continue; // Connected face is already assigned to another group.
3251 if (faceDuplicatesGroupEdge(group, oppositeFace))
3252 continue; // Don't want duplicate edges in a group.
3253 const uint32_t oppositeVertex0 = m_mesh->vertexAt(meshEdgeIndex0(oppositeEdge));
3254 const uint32_t oppositeVertex1 = m_mesh->vertexAt(meshEdgeIndex1(oppositeEdge));
3255 if (bestConnectedFace == UINT32_MAX || (oppositeVertex0 == edgeIt.vertex1() && oppositeVertex1 == edgeIt.vertex0()))
3256 bestConnectedFace = oppositeFace;
3257 #if 0
3258 else {
3259 // Choose the opposite face with the smallest dihedral angle.
3260 const float d1 = 1.0f - dot(computeFaceNormal(f), computeFaceNormal(bestConnectedFace));
3261 const float d2 = 1.0f - dot(computeFaceNormal(f), computeFaceNormal(oppositeFace));
3262 if (d2 < d1)
3263 bestConnectedFace = oppositeFace;
3264 }
3265 #endif
3266 }
3267 if (!alreadyAssignedToThisGroup && bestConnectedFace != UINT32_MAX) {
3268 m_groups[bestConnectedFace] = group;
3269 m_nextFace[bestConnectedFace] = UINT32_MAX;
3270 if (prevFace != UINT32_MAX)
3271 m_nextFace[prevFace] = bestConnectedFace;
3272 prevFace = bestConnectedFace;
3273 groupFaceCount++;
3274 growFaces.push_back(bestConnectedFace);
3275 }
3276 }
3277 }
3278 m_faceCount.push_back(groupFaceCount);
3279 group++;
3280 XA_ASSERT(group < kInvalid);
3281 }
3282 }
3283
3284 class Iterator
3285 {
3286 public:
Iterator(const MeshFaceGroups * meshFaceGroups,Handle group)3287 Iterator(const MeshFaceGroups *meshFaceGroups, Handle group) : m_meshFaceGroups(meshFaceGroups)
3288 {
3289 XA_DEBUG_ASSERT(group != kInvalid);
3290 m_current = m_meshFaceGroups->m_firstFace[group];
3291 }
3292
advance()3293 void advance()
3294 {
3295 m_current = m_meshFaceGroups->m_nextFace[m_current];
3296 }
3297
isDone() const3298 bool isDone() const
3299 {
3300 return m_current == UINT32_MAX;
3301 }
3302
face() const3303 uint32_t face() const
3304 {
3305 return m_current;
3306 }
3307
3308 private:
3309 const MeshFaceGroups *m_meshFaceGroups;
3310 uint32_t m_current;
3311 };
3312
3313 private:
3314 // Check if the face duplicates any edges of any face already in the group.
faceDuplicatesGroupEdgexatlas::internal::MeshFaceGroups3315 bool faceDuplicatesGroupEdge(Handle group, uint32_t face) const
3316 {
3317 for (Mesh::FaceEdgeIterator edgeIt(m_mesh, face); !edgeIt.isDone(); edgeIt.advance()) {
3318 for (Mesh::ColocalEdgeIterator colocalEdgeIt(m_mesh, edgeIt.vertex0(), edgeIt.vertex1()); !colocalEdgeIt.isDone(); colocalEdgeIt.advance()) {
3319 if (m_groups[meshEdgeFace(colocalEdgeIt.edge())] == group)
3320 return true;
3321 }
3322 }
3323 return false;
3324 }
3325
3326 const Mesh *m_mesh;
3327 Array<Handle> m_groups;
3328 Array<uint32_t> m_firstFace;
3329 Array<uint32_t> m_nextFace; // In: face. Out: the next face in the same group.
3330 Array<uint32_t> m_faceCount; // In: face group. Out: number of faces in the group.
3331 };
3332
3333 constexpr MeshFaceGroups::Handle MeshFaceGroups::kInvalid;
3334
meshCloseHole(Mesh * mesh,const Array<uint32_t> & holeVertices,const Vector3 & normal)3335 static bool meshCloseHole(Mesh *mesh, const Array<uint32_t> &holeVertices, const Vector3 &normal)
3336 {
3337 #if XA_CLOSE_HOLES_CHECK_EDGE_INTERSECTION
3338 const uint32_t faceCount = mesh->faceCount();
3339 #endif
3340 const bool compareNormal = equal(normal, Vector3(0.0f), FLT_EPSILON);
3341 uint32_t frontCount = holeVertices.size();
3342 Array<uint32_t> frontVertices;
3343 Array<Vector3> frontPoints;
3344 Array<float> frontAngles;
3345 frontVertices.resize(frontCount);
3346 frontPoints.resize(frontCount);
3347 for (uint32_t i = 0; i < frontCount; i++) {
3348 frontVertices[i] = holeVertices[i];
3349 frontPoints[i] = mesh->position(frontVertices[i]);
3350 }
3351 while (frontCount >= 3) {
3352 frontAngles.resize(frontCount);
3353 float smallestAngle = kPi2, smallestAngleIgnoringNormal = kPi2;
3354 uint32_t smallestAngleIndex = UINT32_MAX, smallestAngleIndexIgnoringNormal = UINT32_MAX;
3355 for (uint32_t i = 0; i < frontCount; i++) {
3356 const uint32_t i1 = i == 0 ? frontCount - 1 : i - 1;
3357 const uint32_t i2 = i;
3358 const uint32_t i3 = (i + 1) % frontCount;
3359 const Vector3 edge1 = frontPoints[i1] - frontPoints[i2];
3360 const Vector3 edge2 = frontPoints[i3] - frontPoints[i2];
3361 frontAngles[i] = atan2f(length(cross(edge1, edge2)), dot(edge1, edge2));
3362 if (frontAngles[i] >= smallestAngle || isNan(frontAngles[i]))
3363 continue;
3364 // Don't duplicate edges.
3365 if (mesh->findEdge(frontVertices[i1], frontVertices[i2]) != UINT32_MAX)
3366 continue;
3367 if (mesh->findEdge(frontVertices[i2], frontVertices[i3]) != UINT32_MAX)
3368 continue;
3369 if (mesh->findEdge(frontVertices[i3], frontVertices[i1]) != UINT32_MAX)
3370 continue;
3371 /*
3372 Make sure he new edge that would be formed by (i3, i1) doesn't intersect any vertices. This often happens when fixing t-junctions.
3373
3374 i2
3375 *
3376 / \
3377 / \
3378 i1 *--*--* i3
3379 \ | /
3380 \|/
3381 *
3382 */
3383 bool intersection = false;
3384 for (uint32_t j = 0; j < frontCount; j++) {
3385 if (j == i1 || j == i2 || j == i3)
3386 continue;
3387 if (lineIntersectsPoint(frontPoints[j], frontPoints[i3], frontPoints[i1], nullptr, mesh->epsilon())) {
3388 intersection = true;
3389 break;
3390 }
3391 }
3392 if (intersection)
3393 continue;
3394 // Don't add the triangle if a boundary point lies on the same plane as the triangle, and is inside it.
3395 intersection = false;
3396 const Plane plane(frontPoints[i1], frontPoints[i2], frontPoints[i3]);
3397 for (uint32_t j = 0; j < frontCount; j++) {
3398 if (j == i1 || j == i2 || j == i3)
3399 continue;
3400 if (!isZero(plane.distance(frontPoints[j]), mesh->epsilon()))
3401 continue;
3402 if (pointInTriangle(frontPoints[j], frontPoints[i1], frontPoints[i2], frontPoints[i3])) {
3403 intersection = true;
3404 break;
3405 }
3406 }
3407 if (intersection)
3408 continue;
3409 #if XA_CLOSE_HOLES_CHECK_EDGE_INTERSECTION
3410 // Don't add the triangle if the new edge (i3, i1), intersects any other triangle that isn't part of the filled hole.
3411 intersection = false;
3412 const Vector3 newEdgeVector = frontPoints[i1] - frontPoints[i3];
3413 for (uint32_t f = 0; f < faceCount; f++) {
3414 Vector3 tri[3];
3415 for (uint32_t j = 0; j < 3; j++)
3416 tri[j] = mesh->position(mesh->vertexAt(f * 3 + j));
3417 float t;
3418 if (rayIntersectsTriangle(frontPoints[i3], newEdgeVector, tri, &t)) {
3419 intersection = true;
3420 break;
3421 }
3422 }
3423 if (intersection)
3424 continue;
3425 #endif
3426 // Skip backwards facing triangles.
3427 if (compareNormal) {
3428 if (frontAngles[i] < smallestAngleIgnoringNormal) {
3429 smallestAngleIgnoringNormal = frontAngles[i];
3430 smallestAngleIndexIgnoringNormal = i;
3431 }
3432 const Vector3 e0 = frontPoints[i3] - frontPoints[i1];
3433 const Vector3 e1 = frontPoints[i2] - frontPoints[i1];
3434 const Vector3 triNormal = normalizeSafe(cross(e0, e1), Vector3(0.0f), mesh->epsilon());
3435 if (dot(normal, triNormal) <= 0.0f)
3436 continue;
3437 }
3438 smallestAngle = smallestAngleIgnoringNormal = frontAngles[i];
3439 smallestAngleIndex = smallestAngleIndexIgnoringNormal = i;
3440 }
3441 // Closing holes failed if we don't have a smallest angle.
3442 // Fallback to ignoring the backwards facing normal test if possible.
3443 if (smallestAngleIndex == UINT32_MAX || smallestAngle <= 0.0f || smallestAngle >= kPi) {
3444 if (smallestAngleIgnoringNormal == UINT32_MAX || smallestAngleIgnoringNormal <= 0.0f || smallestAngleIgnoringNormal >= kPi)
3445 return false;
3446 else
3447 smallestAngleIndex = smallestAngleIndexIgnoringNormal;
3448 }
3449 const uint32_t i1 = smallestAngleIndex == 0 ? frontCount - 1 : smallestAngleIndex - 1;
3450 const uint32_t i2 = smallestAngleIndex;
3451 const uint32_t i3 = (smallestAngleIndex + 1) % frontCount;
3452 const Mesh::AddFaceResult::Enum result = mesh->addFace(frontVertices[i1], frontVertices[i2], frontVertices[i3]);
3453 XA_DEBUG_ASSERT(result == Mesh::AddFaceResult::OK); // Shouldn't happen due to the findEdge calls above.
3454 XA_UNUSED(result);
3455 frontVertices.removeAt(i2);
3456 frontPoints.removeAt(i2);
3457 frontCount = frontVertices.size();
3458 }
3459 return true;
3460 }
3461
meshCloseHoles(Mesh * mesh,const Array<uint32_t> & boundaryLoops,const Vector3 & normal,uint32_t * holeCount,Array<uint32_t> * holeFaceCounts)3462 static bool meshCloseHoles(Mesh *mesh, const Array<uint32_t> &boundaryLoops, const Vector3 &normal, uint32_t *holeCount, Array<uint32_t> *holeFaceCounts)
3463 {
3464 if (holeFaceCounts)
3465 holeFaceCounts->clear();
3466 // Compute lengths.
3467 const uint32_t boundaryCount = boundaryLoops.size();
3468 Array<float> boundaryLengths;
3469 Array<uint32_t> boundaryEdgeCounts;
3470 boundaryEdgeCounts.resize(boundaryCount);
3471 for (uint32_t i = 0; i < boundaryCount; i++) {
3472 float boundaryLength = 0.0f;
3473 boundaryEdgeCounts[i] = 0;
3474 for (Mesh::BoundaryLoopEdgeIterator it(mesh, boundaryLoops[i]); !it.isDone(); it.advance()) {
3475 const Vector3 &t0 = mesh->position(mesh->vertexAt(meshEdgeIndex0(it.edge())));
3476 const Vector3 &t1 = mesh->position(mesh->vertexAt(meshEdgeIndex1(it.edge())));
3477 boundaryLength += length(t1 - t0);
3478 boundaryEdgeCounts[i]++;
3479 }
3480 boundaryLengths.push_back(boundaryLength);
3481 }
3482 // Find disk boundary.
3483 uint32_t diskBoundary = 0;
3484 float maxLength = boundaryLengths[0];
3485 for (uint32_t i = 1; i < boundaryCount; i++) {
3486 if (boundaryLengths[i] > maxLength) {
3487 maxLength = boundaryLengths[i];
3488 diskBoundary = i;
3489 }
3490 }
3491 // Close holes.
3492 Array<uint32_t> holeVertices;
3493 Array<Vector3> holePoints;
3494 bool result = true;
3495 for (uint32_t i = 0; i < boundaryCount; i++) {
3496 if (diskBoundary == i)
3497 continue; // Skip disk boundary.
3498 holeVertices.resize(boundaryEdgeCounts[i]);
3499 holePoints.resize(boundaryEdgeCounts[i]);
3500 // Winding is backwards for internal boundaries.
3501 uint32_t e = 0;
3502 for (Mesh::BoundaryLoopEdgeIterator it(mesh, boundaryLoops[i]); !it.isDone(); it.advance()) {
3503 const uint32_t vertex = mesh->vertexAt(meshEdgeIndex0(it.edge()));
3504 holeVertices[boundaryEdgeCounts[i] - 1 - e] = vertex;
3505 holePoints[boundaryEdgeCounts[i] - 1 - e] = mesh->position(vertex);
3506 e++;
3507 }
3508 const uint32_t oldFaceCount = mesh->faceCount();
3509 if (!meshCloseHole(mesh, holeVertices, normal))
3510 result = false; // Return false if any hole failed to close, but keep trying to close other holes.
3511 if (holeCount)
3512 (*holeCount)++;
3513 if (holeFaceCounts)
3514 holeFaceCounts->push_back(mesh->faceCount() - oldFaceCount);
3515 }
3516 return result;
3517 }
3518
meshIsPlanar(const Mesh & mesh)3519 static bool meshIsPlanar(const Mesh &mesh)
3520 {
3521 const Vector3 p1 = mesh.position(mesh.vertexAt(0));
3522 const Vector3 p2 = mesh.position(mesh.vertexAt(1));
3523 const Vector3 p3 = mesh.position(mesh.vertexAt(2));
3524 const Plane plane(p1, p2, p3);
3525 const uint32_t vertexCount = mesh.vertexCount();
3526 for (uint32_t v = 0; v < vertexCount; v++) {
3527 const float d = plane.distance(mesh.position(v));
3528 if (!isZero(d, mesh.epsilon()))
3529 return false;
3530 }
3531 return true;
3532 }
3533
3534 /*
3535 Fixing T-junctions.
3536
3537 - Find T-junctions. Find vertices that are on an edge.
3538 - This test is approximate.
3539 - Insert edges on a spatial index to speedup queries.
3540 - Consider only open edges, that is edges that have no pairs.
3541 - Consider only vertices on boundaries.
3542 - Close T-junction.
3543 - Split edge.
3544
3545 */
3546 struct SplitEdge
3547 {
3548 uint32_t edge;
3549 float t;
3550 uint32_t vertex;
3551
operator <xatlas::internal::SplitEdge3552 bool operator<(const SplitEdge &other) const
3553 {
3554 if (edge < other.edge)
3555 return true;
3556 else if (edge == other.edge) {
3557 if (t < other.t)
3558 return true;
3559 }
3560 return false;
3561 }
3562 };
3563
3564 // Returns nullptr if there were no t-junctions to fix.
meshFixTJunctions(const Mesh & inputMesh,bool * duplicatedEdge,bool * failed,uint32_t * fixedTJunctionsCount)3565 static Mesh *meshFixTJunctions(const Mesh &inputMesh, bool *duplicatedEdge, bool *failed, uint32_t *fixedTJunctionsCount)
3566 {
3567 if (duplicatedEdge)
3568 *duplicatedEdge = false;
3569 if (failed)
3570 *failed = false;
3571 Array<SplitEdge> splitEdges;
3572 const uint32_t vertexCount = inputMesh.vertexCount();
3573 const uint32_t edgeCount = inputMesh.edgeCount();
3574 for (uint32_t v = 0; v < vertexCount; v++) {
3575 if (!inputMesh.isBoundaryVertex(v))
3576 continue;
3577 // Find edges that this vertex overlaps with.
3578 const Vector3 &pos = inputMesh.position(v);
3579 for (uint32_t e = 0; e < edgeCount; e++) {
3580 if (!inputMesh.isBoundaryEdge(e))
3581 continue;
3582 const Vector3 &edgePos1 = inputMesh.position(inputMesh.vertexAt(meshEdgeIndex0(e)));
3583 const Vector3 &edgePos2 = inputMesh.position(inputMesh.vertexAt(meshEdgeIndex1(e)));
3584 float t;
3585 if (!lineIntersectsPoint(pos, edgePos1, edgePos2, &t, inputMesh.epsilon()))
3586 continue;
3587 SplitEdge splitEdge;
3588 splitEdge.edge = e;
3589 splitEdge.t = t;
3590 splitEdge.vertex = v;
3591 splitEdges.push_back(splitEdge);
3592 }
3593 }
3594 if (splitEdges.isEmpty())
3595 return nullptr;
3596 const uint32_t faceCount = inputMesh.faceCount();
3597 Mesh *mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, inputMesh.epsilon(), vertexCount + splitEdges.size(), faceCount);
3598 for (uint32_t v = 0; v < vertexCount; v++)
3599 mesh->addVertex(inputMesh.position(v));
3600 Array<uint32_t> indexArray;
3601 indexArray.reserve(4);
3602 Array<SplitEdge> faceSplitEdges;
3603 faceSplitEdges.reserve(4);
3604 for (uint32_t f = 0; f < faceCount; f++) {
3605 // Find t-junctions in this face.
3606 faceSplitEdges.clear();
3607 for (uint32_t i = 0; i < splitEdges.size(); i++) {
3608 if (meshEdgeFace(splitEdges[i].edge) == f)
3609 faceSplitEdges.push_back(splitEdges[i]);
3610 }
3611 if (!faceSplitEdges.isEmpty()) {
3612 // Need to split edges in winding order when a single edge has multiple t-junctions.
3613 insertionSort(faceSplitEdges.data(), faceSplitEdges.size());
3614 indexArray.clear();
3615 for (Mesh::FaceEdgeIterator it(&inputMesh, f); !it.isDone(); it.advance()) {
3616 indexArray.push_back(it.vertex0());
3617 for (uint32_t se = 0; se < faceSplitEdges.size(); se++) {
3618 const SplitEdge &splitEdge = faceSplitEdges[se];
3619 if (splitEdge.edge == it.edge())
3620 indexArray.push_back(splitEdge.vertex);
3621 }
3622 }
3623 if (!meshCloseHole(mesh, indexArray, Vector3(0.0f))) {
3624 if (failed)
3625 *failed = true;
3626 }
3627 } else {
3628 // No t-junctions in this face. Copy from input mesh.
3629 if (mesh->addFace(&inputMesh.indices()[f * 3]) == Mesh::AddFaceResult::DuplicateEdge) {
3630 if (duplicatedEdge)
3631 *duplicatedEdge = true;
3632 }
3633 }
3634 }
3635 if (fixedTJunctionsCount)
3636 *fixedTJunctionsCount = splitEdges.size();
3637 return mesh;
3638 }
3639
3640 // boundaryLoops are the first edges for each boundary loop.
meshGetBoundaryLoops(const Mesh & mesh,Array<uint32_t> & boundaryLoops)3641 static void meshGetBoundaryLoops(const Mesh &mesh, Array<uint32_t> &boundaryLoops)
3642 {
3643 const uint32_t edgeCount = mesh.edgeCount();
3644 BitArray bitFlags(edgeCount);
3645 bitFlags.zeroOutMemory();
3646 boundaryLoops.clear();
3647 // Search for boundary edges. Mark all the edges that belong to the same boundary.
3648 for (uint32_t e = 0; e < edgeCount; e++) {
3649 if (bitFlags.get(e) || !mesh.isBoundaryEdge(e))
3650 continue;
3651 for (Mesh::BoundaryLoopEdgeIterator it(&mesh, e); !it.isDone(); it.advance())
3652 bitFlags.set(it.edge());
3653 boundaryLoops.push_back(e);
3654 }
3655 }
3656
3657 struct Progress
3658 {
Progressxatlas::internal::Progress3659 Progress(ProgressCategory::Enum category, ProgressFunc func, void *userData, uint32_t maxValue) : value(0), cancel(false), m_category(category), m_func(func), m_userData(userData), m_maxValue(maxValue), m_progress(0)
3660 {
3661 if (m_func) {
3662 if (!m_func(category, 0, userData))
3663 cancel = true;
3664 }
3665 }
3666
~Progressxatlas::internal::Progress3667 ~Progress()
3668 {
3669 if (m_func) {
3670 if (!m_func(m_category, 100, m_userData))
3671 cancel = true;
3672 }
3673 }
3674
updatexatlas::internal::Progress3675 void update()
3676 {
3677 if (!m_func)
3678 return;
3679 m_mutex.lock();
3680 const uint32_t newProgress = uint32_t(ceilf(value.load() / (float)m_maxValue * 100.0f));
3681 if (newProgress != m_progress && newProgress < 100) {
3682 m_progress = newProgress;
3683 if (!m_func(m_category, m_progress, m_userData))
3684 cancel = true;
3685 }
3686 m_mutex.unlock();
3687 }
3688
setMaxValuexatlas::internal::Progress3689 void setMaxValue(uint32_t maxValue)
3690 {
3691 m_mutex.lock();
3692 m_maxValue = maxValue;
3693 m_mutex.unlock();
3694 }
3695
3696 std::atomic<uint32_t> value;
3697 std::atomic<bool> cancel;
3698
3699 private:
3700 ProgressCategory::Enum m_category;
3701 ProgressFunc m_func;
3702 void *m_userData;
3703 uint32_t m_maxValue;
3704 uint32_t m_progress;
3705 std::mutex m_mutex;
3706 };
3707
3708 struct Spinlock
3709 {
lockxatlas::internal::Spinlock3710 void lock() { while(m_lock.test_and_set(std::memory_order_acquire)) {} }
unlockxatlas::internal::Spinlock3711 void unlock() { m_lock.clear(std::memory_order_release); }
3712
3713 private:
3714 std::atomic_flag m_lock = ATOMIC_FLAG_INIT;
3715 };
3716
3717 struct TaskGroupHandle
3718 {
3719 uint32_t value = UINT32_MAX;
3720 };
3721
3722 struct Task
3723 {
3724 void (*func)(void *userData);
3725 void *userData;
3726 };
3727
3728 #if XA_MULTITHREADED
3729 class TaskScheduler
3730 {
3731 public:
TaskScheduler()3732 TaskScheduler() : m_shutdown(false)
3733 {
3734 m_threadIndex = 0;
3735 // Max with current task scheduler usage is 1 per thread + 1 deep nesting, but allow for some slop.
3736 m_maxGroups = std::thread::hardware_concurrency() * 4;
3737 m_groups = XA_ALLOC_ARRAY(MemTag::Default, TaskGroup, m_maxGroups);
3738 for (uint32_t i = 0; i < m_maxGroups; i++) {
3739 new (&m_groups[i]) TaskGroup();
3740 m_groups[i].free = true;
3741 m_groups[i].ref = 0;
3742 }
3743 m_workers.resize(std::thread::hardware_concurrency() <= 1 ? 1 : std::thread::hardware_concurrency() - 1);
3744 for (uint32_t i = 0; i < m_workers.size(); i++) {
3745 new (&m_workers[i]) Worker();
3746 m_workers[i].wakeup = false;
3747 m_workers[i].thread = XA_NEW_ARGS(MemTag::Default, std::thread, workerThread, this, &m_workers[i], i + 1);
3748 }
3749 }
3750
~TaskScheduler()3751 ~TaskScheduler()
3752 {
3753 m_shutdown = true;
3754 for (uint32_t i = 0; i < m_workers.size(); i++) {
3755 Worker &worker = m_workers[i];
3756 XA_DEBUG_ASSERT(worker.thread);
3757 worker.wakeup = true;
3758 worker.cv.notify_one();
3759 if (worker.thread->joinable())
3760 worker.thread->join();
3761 worker.thread->~thread();
3762 XA_FREE(worker.thread);
3763 worker.~Worker();
3764 }
3765 for (uint32_t i = 0; i < m_maxGroups; i++)
3766 m_groups[i].~TaskGroup();
3767 XA_FREE(m_groups);
3768 }
3769
threadCount() const3770 uint32_t threadCount() const
3771 {
3772 return max(1u, std::thread::hardware_concurrency()); // Including the main thread.
3773 }
3774
createTaskGroup(uint32_t reserveSize=0)3775 TaskGroupHandle createTaskGroup(uint32_t reserveSize = 0)
3776 {
3777 // Claim the first free group.
3778 for (uint32_t i = 0; i < m_maxGroups; i++) {
3779 TaskGroup &group = m_groups[i];
3780 bool expected = true;
3781 if (!group.free.compare_exchange_strong(expected, false))
3782 continue;
3783 group.queueLock.lock();
3784 group.queueHead = 0;
3785 group.queue.clear();
3786 group.queue.reserve(reserveSize);
3787 group.queueLock.unlock();
3788 TaskGroupHandle handle;
3789 handle.value = i;
3790 return handle;
3791 }
3792 XA_DEBUG_ASSERT(false);
3793 TaskGroupHandle handle;
3794 handle.value = UINT32_MAX;
3795 return handle;
3796 }
3797
run(TaskGroupHandle handle,const Task & task)3798 void run(TaskGroupHandle handle, const Task &task)
3799 {
3800 XA_DEBUG_ASSERT(handle.value != UINT32_MAX);
3801 TaskGroup &group = m_groups[handle.value];
3802 group.queueLock.lock();
3803 group.queue.push_back(task);
3804 group.queueLock.unlock();
3805 group.ref++;
3806 // Wake up a worker to run this task.
3807 for (uint32_t i = 0; i < m_workers.size(); i++) {
3808 m_workers[i].wakeup = true;
3809 m_workers[i].cv.notify_one();
3810 }
3811 }
3812
wait(TaskGroupHandle * handle)3813 void wait(TaskGroupHandle *handle)
3814 {
3815 if (handle->value == UINT32_MAX) {
3816 XA_DEBUG_ASSERT(false);
3817 return;
3818 }
3819 // Run tasks from the group queue until empty.
3820 TaskGroup &group = m_groups[handle->value];
3821 for (;;) {
3822 Task *task = nullptr;
3823 group.queueLock.lock();
3824 if (group.queueHead < group.queue.size())
3825 task = &group.queue[group.queueHead++];
3826 group.queueLock.unlock();
3827 if (!task)
3828 break;
3829 task->func(task->userData);
3830 group.ref--;
3831 }
3832 // Even though the task queue is empty, workers can still be running tasks.
3833 while (group.ref > 0)
3834 std::this_thread::yield();
3835 group.free = true;
3836 handle->value = UINT32_MAX;
3837 }
3838
currentThreadIndex()3839 static uint32_t currentThreadIndex() { return m_threadIndex; }
3840
3841 private:
3842 struct TaskGroup
3843 {
3844 std::atomic<bool> free;
3845 Array<Task> queue; // Items are never removed. queueHead is incremented to pop items.
3846 uint32_t queueHead = 0;
3847 Spinlock queueLock;
3848 std::atomic<uint32_t> ref; // Increment when a task is enqueued, decrement when a task finishes.
3849 };
3850
3851 struct Worker
3852 {
3853 std::thread *thread = nullptr;
3854 std::mutex mutex;
3855 std::condition_variable cv;
3856 std::atomic<bool> wakeup;
3857 };
3858
3859 TaskGroup *m_groups;
3860 Array<Worker> m_workers;
3861 std::atomic<bool> m_shutdown;
3862 uint32_t m_maxGroups;
3863 static thread_local uint32_t m_threadIndex;
3864
workerThread(TaskScheduler * scheduler,Worker * worker,uint32_t threadIndex)3865 static void workerThread(TaskScheduler *scheduler, Worker *worker, uint32_t threadIndex)
3866 {
3867 m_threadIndex = threadIndex;
3868 std::unique_lock<std::mutex> lock(worker->mutex);
3869 for (;;) {
3870 worker->cv.wait(lock, [=]{ return worker->wakeup.load(); });
3871 worker->wakeup = false;
3872 for (;;) {
3873 if (scheduler->m_shutdown)
3874 return;
3875 // Look for a task in any of the groups and run it.
3876 TaskGroup *group = nullptr;
3877 Task *task = nullptr;
3878 for (uint32_t i = 0; i < scheduler->m_maxGroups; i++) {
3879 group = &scheduler->m_groups[i];
3880 if (group->free || group->ref == 0)
3881 continue;
3882 group->queueLock.lock();
3883 if (group->queueHead < group->queue.size()) {
3884 task = &group->queue[group->queueHead++];
3885 group->queueLock.unlock();
3886 break;
3887 }
3888 group->queueLock.unlock();
3889 }
3890 if (!task)
3891 break;
3892 task->func(task->userData);
3893 group->ref--;
3894 }
3895 }
3896 }
3897 };
3898
3899 thread_local uint32_t TaskScheduler::m_threadIndex;
3900 #else
3901 class TaskScheduler
3902 {
3903 public:
~TaskScheduler()3904 ~TaskScheduler()
3905 {
3906 for (uint32_t i = 0; i < m_groups.size(); i++)
3907 destroyGroup({ i });
3908 }
3909
threadCount() const3910 uint32_t threadCount() const
3911 {
3912 return 1;
3913 }
3914
createTaskGroup(uint32_t reserveSize=0)3915 TaskGroupHandle createTaskGroup(uint32_t reserveSize = 0)
3916 {
3917 TaskGroup *group = XA_NEW(MemTag::Default, TaskGroup);
3918 group->queue.reserve(reserveSize);
3919 m_groups.push_back(group);
3920 TaskGroupHandle handle;
3921 handle.value = m_groups.size() - 1;
3922 return handle;
3923 }
3924
run(TaskGroupHandle handle,Task task)3925 void run(TaskGroupHandle handle, Task task)
3926 {
3927 m_groups[handle.value]->queue.push_back(task);
3928 }
3929
wait(TaskGroupHandle * handle)3930 void wait(TaskGroupHandle *handle)
3931 {
3932 if (handle->value == UINT32_MAX) {
3933 XA_DEBUG_ASSERT(false);
3934 return;
3935 }
3936 TaskGroup *group = m_groups[handle->value];
3937 for (uint32_t i = 0; i < group->queue.size(); i++)
3938 group->queue[i].func(group->queue[i].userData);
3939 group->queue.clear();
3940 destroyGroup(*handle);
3941 handle->value = UINT32_MAX;
3942 }
3943
currentThreadIndex()3944 static uint32_t currentThreadIndex() { return 0; }
3945
3946 private:
destroyGroup(TaskGroupHandle handle)3947 void destroyGroup(TaskGroupHandle handle)
3948 {
3949 TaskGroup *group = m_groups[handle.value];
3950 if (group) {
3951 group->~TaskGroup();
3952 XA_FREE(group);
3953 m_groups[handle.value] = nullptr;
3954 }
3955 }
3956
3957 struct TaskGroup
3958 {
3959 Array<Task> queue;
3960 };
3961
3962 Array<TaskGroup *> m_groups;
3963 };
3964 #endif
3965
3966 #if XA_DEBUG_EXPORT_TGA
3967 const uint8_t TGA_TYPE_RGB = 2;
3968 const uint8_t TGA_ORIGIN_UPPER = 0x20;
3969
3970 #pragma pack(push, 1)
3971 struct TgaHeader
3972 {
3973 uint8_t id_length;
3974 uint8_t colormap_type;
3975 uint8_t image_type;
3976 uint16_t colormap_index;
3977 uint16_t colormap_length;
3978 uint8_t colormap_size;
3979 uint16_t x_origin;
3980 uint16_t y_origin;
3981 uint16_t width;
3982 uint16_t height;
3983 uint8_t pixel_size;
3984 uint8_t flags;
3985 enum { Size = 18 };
3986 };
3987 #pragma pack(pop)
3988
WriteTga(const char * filename,const uint8_t * data,uint32_t width,uint32_t height)3989 static void WriteTga(const char *filename, const uint8_t *data, uint32_t width, uint32_t height)
3990 {
3991 XA_DEBUG_ASSERT(sizeof(TgaHeader) == TgaHeader::Size);
3992 FILE *f;
3993 XA_FOPEN(f, filename, "wb");
3994 if (!f)
3995 return;
3996 TgaHeader tga;
3997 tga.id_length = 0;
3998 tga.colormap_type = 0;
3999 tga.image_type = TGA_TYPE_RGB;
4000 tga.colormap_index = 0;
4001 tga.colormap_length = 0;
4002 tga.colormap_size = 0;
4003 tga.x_origin = 0;
4004 tga.y_origin = 0;
4005 tga.width = (uint16_t)width;
4006 tga.height = (uint16_t)height;
4007 tga.pixel_size = 24;
4008 tga.flags = TGA_ORIGIN_UPPER;
4009 fwrite(&tga, sizeof(TgaHeader), 1, f);
4010 fwrite(data, sizeof(uint8_t), width * height * 3, f);
4011 fclose(f);
4012 }
4013 #endif
4014
4015 template<typename T>
4016 class ThreadLocal
4017 {
4018 public:
ThreadLocal()4019 ThreadLocal()
4020 {
4021 #if XA_MULTITHREADED
4022 const uint32_t n = std::thread::hardware_concurrency();
4023 #else
4024 const uint32_t n = 1;
4025 #endif
4026 m_array = XA_ALLOC_ARRAY(MemTag::Default, T, n);
4027 for (uint32_t i = 0; i < n; i++)
4028 new (&m_array[i]) T;
4029 }
4030
~ThreadLocal()4031 ~ThreadLocal()
4032 {
4033 #if XA_MULTITHREADED
4034 const uint32_t n = std::thread::hardware_concurrency();
4035 #else
4036 const uint32_t n = 1;
4037 #endif
4038 for (uint32_t i = 0; i < n; i++)
4039 m_array[i].~T();
4040 XA_FREE(m_array);
4041 }
4042
get() const4043 T &get() const
4044 {
4045 return m_array[TaskScheduler::currentThreadIndex()];
4046 }
4047
4048 private:
4049 T *m_array;
4050 };
4051
4052 class UniformGrid2
4053 {
4054 public:
reset(const Vector2 * positions,const uint32_t * indices=nullptr,uint32_t reserveEdgeCount=0)4055 void reset(const Vector2 *positions, const uint32_t *indices = nullptr, uint32_t reserveEdgeCount = 0)
4056 {
4057 m_edges.clear();
4058 if (reserveEdgeCount > 0)
4059 m_edges.reserve(reserveEdgeCount);
4060 m_positions = positions;
4061 m_indices = indices;
4062 m_cellDataOffsets.clear();
4063 }
4064
append(uint32_t edge)4065 void append(uint32_t edge)
4066 {
4067 XA_DEBUG_ASSERT(m_cellDataOffsets.isEmpty());
4068 m_edges.push_back(edge);
4069 }
4070
intersect(Vector2 v1,Vector2 v2,float epsilon)4071 bool intersect(Vector2 v1, Vector2 v2, float epsilon)
4072 {
4073 const uint32_t edgeCount = m_edges.size();
4074 bool bruteForce = edgeCount <= 20;
4075 if (!bruteForce && m_cellDataOffsets.isEmpty())
4076 bruteForce = !createGrid();
4077 if (bruteForce) {
4078 for (uint32_t j = 0; j < edgeCount; j++) {
4079 const uint32_t edge = m_edges[j];
4080 if (linesIntersect(v1, v2, edgePosition0(edge), edgePosition1(edge), epsilon))
4081 return true;
4082 }
4083 } else {
4084 computePotentialEdges(v1, v2);
4085 uint32_t prevEdge = UINT32_MAX;
4086 for (uint32_t j = 0; j < m_potentialEdges.size(); j++) {
4087 const uint32_t edge = m_potentialEdges[j];
4088 if (edge == prevEdge)
4089 continue;
4090 if (linesIntersect(v1, v2, edgePosition0(edge), edgePosition1(edge), epsilon))
4091 return true;
4092 prevEdge = edge;
4093 }
4094 }
4095 return false;
4096 }
4097
4098 // If edges is empty, checks for intersection with all edges in the grid.
intersect(float epsilon,ConstArrayView<uint32_t> edges=ConstArrayView<uint32_t> (),ConstArrayView<uint32_t> ignoreEdges=ConstArrayView<uint32_t> ())4099 bool intersect(float epsilon, ConstArrayView<uint32_t> edges = ConstArrayView<uint32_t>(), ConstArrayView<uint32_t> ignoreEdges = ConstArrayView<uint32_t>())
4100 {
4101 bool bruteForce = m_edges.size() <= 20;
4102 if (!bruteForce && m_cellDataOffsets.isEmpty())
4103 bruteForce = !createGrid();
4104 const uint32_t *edges1, *edges2 = nullptr;
4105 uint32_t edges1Count, edges2Count = 0;
4106 if (edges.length == 0) {
4107 edges1 = m_edges.data();
4108 edges1Count = m_edges.size();
4109 } else {
4110 edges1 = edges.data;
4111 edges1Count = edges.length;
4112 }
4113 if (bruteForce) {
4114 edges2 = m_edges.data();
4115 edges2Count = m_edges.size();
4116 }
4117 for (uint32_t i = 0; i < edges1Count; i++) {
4118 const uint32_t edge1 = edges1[i];
4119 const uint32_t edge1Vertex[2] = { vertexAt(meshEdgeIndex0(edge1)), vertexAt(meshEdgeIndex1(edge1)) };
4120 const Vector2 &edge1Position1 = m_positions[edge1Vertex[0]];
4121 const Vector2 &edge1Position2 = m_positions[edge1Vertex[1]];
4122 const Extents2 edge1Extents(edge1Position1, edge1Position2);
4123 uint32_t j = 0;
4124 if (bruteForce) {
4125 // If checking against self, test each edge pair only once.
4126 if (edges.length == 0) {
4127 j = i + 1;
4128 if (j == edges1Count)
4129 break;
4130 }
4131 } else {
4132 computePotentialEdges(edgePosition0(edge1), edgePosition1(edge1));
4133 edges2 = m_potentialEdges.data();
4134 edges2Count = m_potentialEdges.size();
4135 }
4136 uint32_t prevEdge = UINT32_MAX; // Handle potential edges duplicates.
4137 for (; j < edges2Count; j++) {
4138 const uint32_t edge2 = edges2[j];
4139 if (edge1 == edge2)
4140 continue;
4141 if (edge2 == prevEdge)
4142 continue;
4143 prevEdge = edge2;
4144 // Check if edge2 is ignored.
4145 bool ignore = false;
4146 for (uint32_t k = 0; k < ignoreEdges.length; k++) {
4147 if (edge2 == ignoreEdges[k]) {
4148 ignore = true;
4149 break;
4150 }
4151 }
4152 if (ignore)
4153 continue;
4154 const uint32_t edge2Vertex[2] = { vertexAt(meshEdgeIndex0(edge2)), vertexAt(meshEdgeIndex1(edge2)) };
4155 // Ignore connected edges, since they can't intersect (only overlap), and may be detected as false positives.
4156 if (edge1Vertex[0] == edge2Vertex[0] || edge1Vertex[0] == edge2Vertex[1] || edge1Vertex[1] == edge2Vertex[0] || edge1Vertex[1] == edge2Vertex[1])
4157 continue;
4158 const Vector2 &edge2Position1 = m_positions[edge2Vertex[0]];
4159 const Vector2 &edge2Position2 = m_positions[edge2Vertex[1]];
4160 if (!Extents2::intersect(edge1Extents, Extents2(edge2Position1, edge2Position2)))
4161 continue;
4162 if (linesIntersect(edge1Position1, edge1Position2, edge2Position1, edge2Position2, epsilon))
4163 return true;
4164 }
4165 }
4166 return false;
4167 }
4168
4169 #if XA_DEBUG_EXPORT_BOUNDARY_GRID
debugExport(const char * filename)4170 void debugExport(const char *filename)
4171 {
4172 Array<uint8_t> image;
4173 image.resize(m_gridWidth * m_gridHeight * 3);
4174 for (uint32_t y = 0; y < m_gridHeight; y++) {
4175 for (uint32_t x = 0; x < m_gridWidth; x++) {
4176 uint8_t *bgr = &image[(x + y * m_gridWidth) * 3];
4177 bgr[0] = bgr[1] = bgr[2] = 32;
4178 uint32_t offset = m_cellDataOffsets[x + y * m_gridWidth];
4179 while (offset != UINT32_MAX) {
4180 const uint32_t edge2 = m_cellData[offset];
4181 srand(edge2);
4182 for (uint32_t i = 0; i < 3; i++)
4183 bgr[i] = uint8_t(bgr[i] * 0.5f + (rand() % 255) * 0.5f);
4184 offset = m_cellData[offset + 1];
4185 }
4186 }
4187 }
4188 WriteTga(filename, image.data(), m_gridWidth, m_gridHeight);
4189 }
4190 #endif
4191
4192 private:
createGrid()4193 bool createGrid()
4194 {
4195 // Compute edge extents. Min will be the grid origin.
4196 const uint32_t edgeCount = m_edges.size();
4197 Extents2 edgeExtents;
4198 edgeExtents.reset();
4199 for (uint32_t i = 0; i < edgeCount; i++) {
4200 const uint32_t edge = m_edges[i];
4201 edgeExtents.add(edgePosition0(edge));
4202 edgeExtents.add(edgePosition1(edge));
4203 }
4204 m_gridOrigin = edgeExtents.min;
4205 // Size grid to approximately one edge per cell.
4206 const Vector2 extentsSize(edgeExtents.max - edgeExtents.min);
4207 m_cellSize = min(extentsSize.x, extentsSize.y) / sqrtf((float)edgeCount);
4208 if (m_cellSize <= 0.0f)
4209 return false;
4210 m_gridWidth = uint32_t(ceilf(extentsSize.x / m_cellSize));
4211 m_gridHeight = uint32_t(ceilf(extentsSize.y / m_cellSize));
4212 if (m_gridWidth == 0 || m_gridHeight == 0)
4213 return false;
4214 // Insert edges into cells.
4215 m_cellDataOffsets.resize(m_gridWidth * m_gridHeight);
4216 for (uint32_t i = 0; i < m_cellDataOffsets.size(); i++)
4217 m_cellDataOffsets[i] = UINT32_MAX;
4218 m_cellData.clear();
4219 m_cellData.reserve(edgeCount * 2);
4220 for (uint32_t i = 0; i < edgeCount; i++) {
4221 const uint32_t edge = m_edges[i];
4222 traverse(edgePosition0(edge), edgePosition1(edge));
4223 XA_DEBUG_ASSERT(!m_traversedCellOffsets.isEmpty());
4224 for (uint32_t j = 0; j < m_traversedCellOffsets.size(); j++) {
4225 const uint32_t cell = m_traversedCellOffsets[j];
4226 uint32_t offset = m_cellDataOffsets[cell];
4227 if (offset == UINT32_MAX)
4228 m_cellDataOffsets[cell] = m_cellData.size();
4229 else {
4230 for (;;) {
4231 uint32_t &nextOffset = m_cellData[offset + 1];
4232 if (nextOffset == UINT32_MAX) {
4233 nextOffset = m_cellData.size();
4234 break;
4235 }
4236 offset = nextOffset;
4237 }
4238 }
4239 m_cellData.push_back(edge);
4240 m_cellData.push_back(UINT32_MAX);
4241 }
4242 }
4243 return true;
4244 }
4245
computePotentialEdges(Vector2 p1,Vector2 p2)4246 void computePotentialEdges(Vector2 p1, Vector2 p2)
4247 {
4248 m_potentialEdges.clear();
4249 traverse(p1, p2);
4250 for (uint32_t j = 0; j < m_traversedCellOffsets.size(); j++) {
4251 const uint32_t cell = m_traversedCellOffsets[j];
4252 uint32_t offset = m_cellDataOffsets[cell];
4253 while (offset != UINT32_MAX) {
4254 const uint32_t edge2 = m_cellData[offset];
4255 m_potentialEdges.push_back(edge2);
4256 offset = m_cellData[offset + 1];
4257 }
4258 }
4259 if (m_potentialEdges.isEmpty())
4260 return;
4261 insertionSort(m_potentialEdges.data(), m_potentialEdges.size());
4262 }
4263
4264 // "A Fast Voxel Traversal Algorithm for Ray Tracing"
traverse(Vector2 p1,Vector2 p2)4265 void traverse(Vector2 p1, Vector2 p2)
4266 {
4267 const Vector2 dir = p2 - p1;
4268 const Vector2 normal = normalizeSafe(dir, Vector2(0.0f), kEpsilon);
4269 const int stepX = dir.x >= 0 ? 1 : -1;
4270 const int stepY = dir.y >= 0 ? 1 : -1;
4271 const uint32_t firstCell[2] = { cellX(p1.x), cellY(p1.y) };
4272 const uint32_t lastCell[2] = { cellX(p2.x), cellY(p2.y) };
4273 float distToNextCellX;
4274 if (stepX == 1)
4275 distToNextCellX = (firstCell[0] + 1) * m_cellSize - (p1.x - m_gridOrigin.x);
4276 else
4277 distToNextCellX = (p1.x - m_gridOrigin.x) - firstCell[0] * m_cellSize;
4278 float distToNextCellY;
4279 if (stepY == 1)
4280 distToNextCellY = (firstCell[1] + 1) * m_cellSize - (p1.y - m_gridOrigin.y);
4281 else
4282 distToNextCellY = (p1.y - m_gridOrigin.y) - firstCell[1] * m_cellSize;
4283 float tMaxX, tMaxY, tDeltaX, tDeltaY;
4284 if (normal.x > kEpsilon || normal.x < -kEpsilon) {
4285 tMaxX = (distToNextCellX * stepX) / normal.x;
4286 tDeltaX = (m_cellSize * stepX) / normal.x;
4287 }
4288 else
4289 tMaxX = tDeltaX = FLT_MAX;
4290 if (normal.y > kEpsilon || normal.y < -kEpsilon) {
4291 tMaxY = (distToNextCellY * stepY) / normal.y;
4292 tDeltaY = (m_cellSize * stepY) / normal.y;
4293 }
4294 else
4295 tMaxY = tDeltaY = FLT_MAX;
4296 m_traversedCellOffsets.clear();
4297 m_traversedCellOffsets.push_back(firstCell[0] + firstCell[1] * m_gridWidth);
4298 uint32_t currentCell[2] = { firstCell[0], firstCell[1] };
4299 while (!(currentCell[0] == lastCell[0] && currentCell[1] == lastCell[1])) {
4300 if (tMaxX < tMaxY) {
4301 tMaxX += tDeltaX;
4302 currentCell[0] += stepX;
4303 } else {
4304 tMaxY += tDeltaY;
4305 currentCell[1] += stepY;
4306 }
4307 if (currentCell[0] >= m_gridWidth || currentCell[1] >= m_gridHeight)
4308 break;
4309 if (stepX == -1 && currentCell[0] < lastCell[0])
4310 break;
4311 if (stepX == 1 && currentCell[0] > lastCell[0])
4312 break;
4313 if (stepY == -1 && currentCell[1] < lastCell[1])
4314 break;
4315 if (stepY == 1 && currentCell[1] > lastCell[1])
4316 break;
4317 m_traversedCellOffsets.push_back(currentCell[0] + currentCell[1] * m_gridWidth);
4318 }
4319 }
4320
cellX(float x) const4321 uint32_t cellX(float x) const
4322 {
4323 return min((uint32_t)max(0.0f, (x - m_gridOrigin.x) / m_cellSize), m_gridWidth - 1u);
4324 }
4325
cellY(float y) const4326 uint32_t cellY(float y) const
4327 {
4328 return min((uint32_t)max(0.0f, (y - m_gridOrigin.y) / m_cellSize), m_gridHeight - 1u);
4329 }
4330
edgePosition0(uint32_t edge) const4331 Vector2 edgePosition0(uint32_t edge) const
4332 {
4333 return m_positions[vertexAt(meshEdgeIndex0(edge))];
4334 }
4335
edgePosition1(uint32_t edge) const4336 Vector2 edgePosition1(uint32_t edge) const
4337 {
4338 return m_positions[vertexAt(meshEdgeIndex1(edge))];
4339 }
4340
vertexAt(uint32_t index) const4341 uint32_t vertexAt(uint32_t index) const
4342 {
4343 return m_indices ? m_indices[index] : index;
4344 }
4345
4346 Array<uint32_t> m_edges;
4347 const Vector2 *m_positions;
4348 const uint32_t *m_indices; // Optional
4349 float m_cellSize;
4350 Vector2 m_gridOrigin;
4351 uint32_t m_gridWidth, m_gridHeight; // in cells
4352 Array<uint32_t> m_cellDataOffsets;
4353 Array<uint32_t> m_cellData;
4354 Array<uint32_t> m_potentialEdges;
4355 Array<uint32_t> m_traversedCellOffsets;
4356 };
4357
4358 struct UvMeshChart
4359 {
4360 Array<uint32_t> faces;
4361 Array<uint32_t> indices;
4362 uint32_t material;
4363 };
4364
4365 struct UvMesh
4366 {
4367 UvMeshDecl decl;
4368 Array<uint32_t> indices;
4369 Array<UvMeshChart *> charts;
4370 Array<uint32_t> vertexToChartMap;
4371 };
4372
4373 struct UvMeshInstance
4374 {
4375 UvMesh *mesh;
4376 Array<Vector2> texcoords;
4377 bool rotateCharts;
4378 };
4379
4380 /*
4381 * Copyright (c) 2004-2010, Bruno Levy
4382 * All rights reserved.
4383 *
4384 * Redistribution and use in source and binary forms, with or without
4385 * modification, are permitted provided that the following conditions are met:
4386 *
4387 * * Redistributions of source code must retain the above copyright notice,
4388 * this list of conditions and the following disclaimer.
4389 * * Redistributions in binary form must reproduce the above copyright notice,
4390 * this list of conditions and the following disclaimer in the documentation
4391 * and/or other materials provided with the distribution.
4392 * * Neither the name of the ALICE Project-Team nor the names of its
4393 * contributors may be used to endorse or promote products derived from this
4394 * software without specific prior written permission.
4395 *
4396 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
4397 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4398 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4399 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
4400 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
4401 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
4402 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
4403 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
4404 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
4405 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
4406 * POSSIBILITY OF SUCH DAMAGE.
4407 *
4408 * If you modify this software, you should include a notice giving the
4409 * name of the person performing the modification, the date of modification,
4410 * and the reason for such modification.
4411 *
4412 * Contact: Bruno Levy
4413 *
4414 * levy@loria.fr
4415 *
4416 * ALICE Project
4417 * LORIA, INRIA Lorraine,
4418 * Campus Scientifique, BP 239
4419 * 54506 VANDOEUVRE LES NANCY CEDEX
4420 * FRANCE
4421 */
4422 namespace opennl {
4423 #define NL_NEW(T) XA_ALLOC(MemTag::OpenNL, T)
4424 #define NL_NEW_ARRAY(T,NB) XA_ALLOC_ARRAY(MemTag::OpenNL, T, NB)
4425 #define NL_RENEW_ARRAY(T,x,NB) XA_REALLOC(MemTag::OpenNL, x, T, NB)
4426 #define NL_DELETE(x) XA_FREE(x); x = nullptr
4427 #define NL_DELETE_ARRAY(x) XA_FREE(x); x = nullptr
4428 #define NL_CLEAR(x, T) memset(x, 0, sizeof(T));
4429 #define NL_CLEAR_ARRAY(T,x,NB) memset(x, 0, (size_t)(NB)*sizeof(T))
4430 #define NL_NEW_VECTOR(dim) XA_ALLOC_ARRAY(MemTag::OpenNL, double, dim)
4431 #define NL_DELETE_VECTOR(ptr) XA_FREE(ptr)
4432
4433 struct NLMatrixStruct;
4434 typedef NLMatrixStruct * NLMatrix;
4435 typedef void (*NLDestroyMatrixFunc)(NLMatrix M);
4436 typedef void (*NLMultMatrixVectorFunc)(NLMatrix M, const double* x, double* y);
4437
4438 #define NL_MATRIX_SPARSE_DYNAMIC 0x1001
4439 #define NL_MATRIX_CRS 0x1002
4440 #define NL_MATRIX_OTHER 0x1006
4441
4442 struct NLMatrixStruct
4443 {
4444 uint32_t m;
4445 uint32_t n;
4446 uint32_t type;
4447 NLDestroyMatrixFunc destroy_func;
4448 NLMultMatrixVectorFunc mult_func;
4449 };
4450
4451 /* Dynamic arrays for sparse row/columns */
4452
4453 struct NLCoeff
4454 {
4455 uint32_t index;
4456 double value;
4457 };
4458
4459 struct NLRowColumn
4460 {
4461 uint32_t size;
4462 uint32_t capacity;
4463 NLCoeff* coeff;
4464 };
4465
4466 /* Compressed Row Storage */
4467
4468 struct NLCRSMatrix
4469 {
4470 uint32_t m;
4471 uint32_t n;
4472 uint32_t type;
4473 NLDestroyMatrixFunc destroy_func;
4474 NLMultMatrixVectorFunc mult_func;
4475 double* val;
4476 uint32_t* rowptr;
4477 uint32_t* colind;
4478 uint32_t nslices;
4479 uint32_t* sliceptr;
4480 };
4481
4482 /* SparseMatrix data structure */
4483
4484 struct NLSparseMatrix
4485 {
4486 uint32_t m;
4487 uint32_t n;
4488 uint32_t type;
4489 NLDestroyMatrixFunc destroy_func;
4490 NLMultMatrixVectorFunc mult_func;
4491 uint32_t diag_size;
4492 uint32_t diag_capacity;
4493 NLRowColumn* row;
4494 NLRowColumn* column;
4495 double* diag;
4496 uint32_t row_capacity;
4497 uint32_t column_capacity;
4498 };
4499
4500 /* NLContext data structure */
4501
4502 struct NLBufferBinding
4503 {
4504 void* base_address;
4505 uint32_t stride;
4506 };
4507
4508 #define NL_BUFFER_ITEM(B,i) *(double*)((void*)((char*)((B).base_address)+((i)*(B).stride)))
4509
4510 struct NLContext
4511 {
4512 NLBufferBinding *variable_buffer;
4513 double *variable_value;
4514 bool *variable_is_locked;
4515 uint32_t *variable_index;
4516 uint32_t n;
4517 NLMatrix M;
4518 NLMatrix P;
4519 NLMatrix B;
4520 NLRowColumn af;
4521 NLRowColumn al;
4522 double *x;
4523 double *b;
4524 uint32_t nb_variables;
4525 uint32_t nb_systems;
4526 uint32_t current_row;
4527 uint32_t max_iterations;
4528 bool max_iterations_defined;
4529 double threshold;
4530 double omega;
4531 uint32_t used_iterations;
4532 double error;
4533 };
4534
nlDeleteMatrix(NLMatrix M)4535 static void nlDeleteMatrix(NLMatrix M)
4536 {
4537 if (!M)
4538 return;
4539 M->destroy_func(M);
4540 NL_DELETE(M);
4541 }
4542
nlMultMatrixVector(NLMatrix M,const double * x,double * y)4543 static void nlMultMatrixVector(NLMatrix M, const double* x, double* y)
4544 {
4545 M->mult_func(M, x, y);
4546 }
4547
nlRowColumnConstruct(NLRowColumn * c)4548 static void nlRowColumnConstruct(NLRowColumn* c)
4549 {
4550 c->size = 0;
4551 c->capacity = 0;
4552 c->coeff = nullptr;
4553 }
4554
nlRowColumnDestroy(NLRowColumn * c)4555 static void nlRowColumnDestroy(NLRowColumn* c)
4556 {
4557 NL_DELETE_ARRAY(c->coeff);
4558 c->size = 0;
4559 c->capacity = 0;
4560 }
4561
nlRowColumnGrow(NLRowColumn * c)4562 static void nlRowColumnGrow(NLRowColumn* c)
4563 {
4564 if (c->capacity != 0) {
4565 c->capacity = 2 * c->capacity;
4566 c->coeff = NL_RENEW_ARRAY(NLCoeff, c->coeff, c->capacity);
4567 } else {
4568 c->capacity = 4;
4569 c->coeff = NL_NEW_ARRAY(NLCoeff, c->capacity);
4570 NL_CLEAR_ARRAY(NLCoeff, c->coeff, c->capacity);
4571 }
4572 }
4573
nlRowColumnAdd(NLRowColumn * c,uint32_t index,double value)4574 static void nlRowColumnAdd(NLRowColumn* c, uint32_t index, double value)
4575 {
4576 for (uint32_t i = 0; i < c->size; i++) {
4577 if (c->coeff[i].index == index) {
4578 c->coeff[i].value += value;
4579 return;
4580 }
4581 }
4582 if (c->size == c->capacity)
4583 nlRowColumnGrow(c);
4584 c->coeff[c->size].index = index;
4585 c->coeff[c->size].value = value;
4586 c->size++;
4587 }
4588
4589 /* Does not check whether the index already exists */
nlRowColumnAppend(NLRowColumn * c,uint32_t index,double value)4590 static void nlRowColumnAppend(NLRowColumn* c, uint32_t index, double value)
4591 {
4592 if (c->size == c->capacity)
4593 nlRowColumnGrow(c);
4594 c->coeff[c->size].index = index;
4595 c->coeff[c->size].value = value;
4596 c->size++;
4597 }
4598
nlRowColumnZero(NLRowColumn * c)4599 static void nlRowColumnZero(NLRowColumn* c)
4600 {
4601 c->size = 0;
4602 }
4603
nlRowColumnClear(NLRowColumn * c)4604 static void nlRowColumnClear(NLRowColumn* c)
4605 {
4606 c->size = 0;
4607 c->capacity = 0;
4608 NL_DELETE_ARRAY(c->coeff);
4609 }
4610
nlCoeffCompare(const void * p1,const void * p2)4611 static int nlCoeffCompare(const void* p1, const void* p2)
4612 {
4613 return (((NLCoeff*)(p2))->index < ((NLCoeff*)(p1))->index);
4614 }
4615
nlRowColumnSort(NLRowColumn * c)4616 static void nlRowColumnSort(NLRowColumn* c)
4617 {
4618 qsort(c->coeff, c->size, sizeof(NLCoeff), nlCoeffCompare);
4619 }
4620
4621 /* CRSMatrix data structure */
4622
nlCRSMatrixDestroy(NLCRSMatrix * M)4623 static void nlCRSMatrixDestroy(NLCRSMatrix* M)
4624 {
4625 NL_DELETE_ARRAY(M->val);
4626 NL_DELETE_ARRAY(M->rowptr);
4627 NL_DELETE_ARRAY(M->colind);
4628 NL_DELETE_ARRAY(M->sliceptr);
4629 M->m = 0;
4630 M->n = 0;
4631 M->nslices = 0;
4632 }
4633
nlCRSMatrixMultSlice(NLCRSMatrix * M,const double * x,double * y,uint32_t Ibegin,uint32_t Iend)4634 static void nlCRSMatrixMultSlice(NLCRSMatrix* M, const double* x, double* y, uint32_t Ibegin, uint32_t Iend)
4635 {
4636 for (uint32_t i = Ibegin; i < Iend; ++i) {
4637 double sum = 0.0;
4638 for (uint32_t j = M->rowptr[i]; j < M->rowptr[i + 1]; ++j)
4639 sum += M->val[j] * x[M->colind[j]];
4640 y[i] = sum;
4641 }
4642 }
4643
nlCRSMatrixMult(NLCRSMatrix * M,const double * x,double * y)4644 static void nlCRSMatrixMult(NLCRSMatrix* M, const double* x, double* y)
4645 {
4646 int nslices = (int)(M->nslices);
4647 for (int slice = 0; slice < nslices; ++slice)
4648 nlCRSMatrixMultSlice(M, x, y, M->sliceptr[slice], M->sliceptr[slice + 1]);
4649 }
4650
nlCRSMatrixConstruct(NLCRSMatrix * M,uint32_t m,uint32_t n,uint32_t nnz,uint32_t nslices)4651 static void nlCRSMatrixConstruct(NLCRSMatrix* M, uint32_t m, uint32_t n, uint32_t nnz, uint32_t nslices)
4652 {
4653 M->m = m;
4654 M->n = n;
4655 M->type = NL_MATRIX_CRS;
4656 M->destroy_func = (NLDestroyMatrixFunc)nlCRSMatrixDestroy;
4657 M->mult_func = (NLMultMatrixVectorFunc)nlCRSMatrixMult;
4658 M->nslices = nslices;
4659 M->val = NL_NEW_ARRAY(double, nnz);
4660 NL_CLEAR_ARRAY(double, M->val, nnz);
4661 M->rowptr = NL_NEW_ARRAY(uint32_t, m + 1);
4662 NL_CLEAR_ARRAY(uint32_t, M->rowptr, m + 1);
4663 M->colind = NL_NEW_ARRAY(uint32_t, nnz);
4664 NL_CLEAR_ARRAY(uint32_t, M->colind, nnz);
4665 M->sliceptr = NL_NEW_ARRAY(uint32_t, nslices + 1);
4666 NL_CLEAR_ARRAY(uint32_t, M->sliceptr, nslices + 1);
4667 }
4668
4669 /* SparseMatrix data structure */
4670
nlSparseMatrixDestroyRowColumns(NLSparseMatrix * M)4671 static void nlSparseMatrixDestroyRowColumns(NLSparseMatrix* M)
4672 {
4673 for (uint32_t i = 0; i < M->m; i++)
4674 nlRowColumnDestroy(&(M->row[i]));
4675 NL_DELETE_ARRAY(M->row);
4676 }
4677
nlSparseMatrixDestroy(NLSparseMatrix * M)4678 static void nlSparseMatrixDestroy(NLSparseMatrix* M)
4679 {
4680 XA_DEBUG_ASSERT(M->type == NL_MATRIX_SPARSE_DYNAMIC);
4681 nlSparseMatrixDestroyRowColumns(M);
4682 NL_DELETE_ARRAY(M->diag);
4683 }
4684
nlSparseMatrixAdd(NLSparseMatrix * M,uint32_t i,uint32_t j,double value)4685 static void nlSparseMatrixAdd(NLSparseMatrix* M, uint32_t i, uint32_t j, double value)
4686 {
4687 XA_DEBUG_ASSERT(i >= 0 && i <= M->m - 1);
4688 XA_DEBUG_ASSERT(j >= 0 && j <= M->n - 1);
4689 if (i == j)
4690 M->diag[i] += value;
4691 nlRowColumnAdd(&(M->row[i]), j, value);
4692 }
4693
4694 /* Returns the number of non-zero coefficients */
nlSparseMatrixNNZ(NLSparseMatrix * M)4695 static uint32_t nlSparseMatrixNNZ(NLSparseMatrix* M)
4696 {
4697 uint32_t nnz = 0;
4698 for (uint32_t i = 0; i < M->m; i++)
4699 nnz += M->row[i].size;
4700 return nnz;
4701 }
4702
nlSparseMatrixSort(NLSparseMatrix * M)4703 static void nlSparseMatrixSort(NLSparseMatrix* M)
4704 {
4705 for (uint32_t i = 0; i < M->m; i++)
4706 nlRowColumnSort(&(M->row[i]));
4707 }
4708
4709 /* SparseMatrix x Vector routines, internal helper routines */
4710
nlSparseMatrix_mult_rows(NLSparseMatrix * A,const double * x,double * y)4711 static void nlSparseMatrix_mult_rows(NLSparseMatrix* A, const double* x, double* y)
4712 {
4713 /*
4714 * Note: OpenMP does not like unsigned ints
4715 * (causes some floating point exceptions),
4716 * therefore I use here signed ints for all
4717 * indices.
4718 */
4719 int m = (int)(A->m);
4720 NLCoeff* c = nullptr;
4721 NLRowColumn* Ri = nullptr;
4722 for (int i = 0; i < m; i++) {
4723 Ri = &(A->row[i]);
4724 y[i] = 0;
4725 for (int ij = 0; ij < (int)(Ri->size); ij++) {
4726 c = &(Ri->coeff[ij]);
4727 y[i] += c->value * x[c->index];
4728 }
4729 }
4730 }
4731
nlSparseMatrixMult(NLSparseMatrix * A,const double * x,double * y)4732 static void nlSparseMatrixMult(NLSparseMatrix* A, const double* x, double* y)
4733 {
4734 XA_DEBUG_ASSERT(A->type == NL_MATRIX_SPARSE_DYNAMIC);
4735 nlSparseMatrix_mult_rows(A, x, y);
4736 }
4737
nlSparseMatrixConstruct(NLSparseMatrix * M,uint32_t m,uint32_t n)4738 static void nlSparseMatrixConstruct(NLSparseMatrix* M, uint32_t m, uint32_t n)
4739 {
4740 M->m = m;
4741 M->n = n;
4742 M->type = NL_MATRIX_SPARSE_DYNAMIC;
4743 M->destroy_func = (NLDestroyMatrixFunc)nlSparseMatrixDestroy;
4744 M->mult_func = (NLMultMatrixVectorFunc)nlSparseMatrixMult;
4745 M->row = NL_NEW_ARRAY(NLRowColumn, m);
4746 NL_CLEAR_ARRAY(NLRowColumn, M->row, m);
4747 M->row_capacity = m;
4748 for (uint32_t i = 0; i < n; i++)
4749 nlRowColumnConstruct(&(M->row[i]));
4750 M->row_capacity = 0;
4751 M->column = nullptr;
4752 M->column_capacity = 0;
4753 M->diag_size = min(m, n);
4754 M->diag_capacity = M->diag_size;
4755 M->diag = NL_NEW_ARRAY(double, M->diag_size);
4756 NL_CLEAR_ARRAY(double, M->diag, M->diag_size);
4757 }
4758
nlCRSMatrixNewFromSparseMatrix(NLSparseMatrix * M)4759 static NLMatrix nlCRSMatrixNewFromSparseMatrix(NLSparseMatrix* M)
4760 {
4761 uint32_t nnz = nlSparseMatrixNNZ(M);
4762 uint32_t nslices = 8; /* TODO: get number of cores */
4763 uint32_t slice, cur_bound, cur_NNZ, cur_row;
4764 uint32_t k;
4765 uint32_t slice_size = nnz / nslices;
4766 NLCRSMatrix* CRS = NL_NEW(NLCRSMatrix);
4767 NL_CLEAR(CRS, NLCRSMatrix);
4768 nlCRSMatrixConstruct(CRS, M->m, M->n, nnz, nslices);
4769 nlSparseMatrixSort(M);
4770 /* Convert matrix to CRS format */
4771 k = 0;
4772 for (uint32_t i = 0; i < M->m; ++i) {
4773 NLRowColumn* Ri = &(M->row[i]);
4774 CRS->rowptr[i] = k;
4775 for (uint32_t ij = 0; ij < Ri->size; ij++) {
4776 NLCoeff* c = &(Ri->coeff[ij]);
4777 CRS->val[k] = c->value;
4778 CRS->colind[k] = c->index;
4779 ++k;
4780 }
4781 }
4782 CRS->rowptr[M->m] = k;
4783 /* Create "slices" to be used by parallel sparse matrix vector product */
4784 if (CRS->sliceptr) {
4785 cur_bound = slice_size;
4786 cur_NNZ = 0;
4787 cur_row = 0;
4788 CRS->sliceptr[0] = 0;
4789 for (slice = 1; slice < nslices; ++slice) {
4790 while (cur_NNZ < cur_bound && cur_row < M->m) {
4791 ++cur_row;
4792 cur_NNZ += CRS->rowptr[cur_row + 1] - CRS->rowptr[cur_row];
4793 }
4794 CRS->sliceptr[slice] = cur_row;
4795 cur_bound += slice_size;
4796 }
4797 CRS->sliceptr[nslices] = M->m;
4798 }
4799 return (NLMatrix)CRS;
4800 }
4801
nlMatrixCompress(NLMatrix * M)4802 static void nlMatrixCompress(NLMatrix* M)
4803 {
4804 NLMatrix CRS = nullptr;
4805 if ((*M)->type != NL_MATRIX_SPARSE_DYNAMIC)
4806 return;
4807 CRS = nlCRSMatrixNewFromSparseMatrix((NLSparseMatrix*)*M);
4808 nlDeleteMatrix(*M);
4809 *M = CRS;
4810 }
4811
nlNewContext()4812 static NLContext *nlNewContext()
4813 {
4814 NLContext* result = NL_NEW(NLContext);
4815 NL_CLEAR(result, NLContext);
4816 result->max_iterations = 100;
4817 result->threshold = 1e-6;
4818 result->omega = 1.5;
4819 result->nb_systems = 1;
4820 return result;
4821 }
4822
nlDeleteContext(NLContext * context)4823 static void nlDeleteContext(NLContext *context)
4824 {
4825 nlDeleteMatrix(context->M);
4826 context->M = nullptr;
4827 nlDeleteMatrix(context->P);
4828 context->P = nullptr;
4829 nlDeleteMatrix(context->B);
4830 context->B = nullptr;
4831 nlRowColumnDestroy(&context->af);
4832 nlRowColumnDestroy(&context->al);
4833 NL_DELETE_ARRAY(context->variable_value);
4834 NL_DELETE_ARRAY(context->variable_buffer);
4835 NL_DELETE_ARRAY(context->variable_is_locked);
4836 NL_DELETE_ARRAY(context->variable_index);
4837 NL_DELETE_ARRAY(context->x);
4838 NL_DELETE_ARRAY(context->b);
4839 NL_DELETE(context);
4840 }
4841
ddot(int n,const double * x,const double * y)4842 static double ddot(int n, const double *x, const double *y)
4843 {
4844 double sum = 0.0;
4845 for (int i = 0; i < n; i++)
4846 sum += x[i] * y[i];
4847 return sum;
4848 }
4849
daxpy(int n,double a,const double * x,double * y)4850 static void daxpy(int n, double a, const double *x, double *y)
4851 {
4852 for (int i = 0; i < n; i++)
4853 y[i] = a * x[i] + y[i];
4854 }
4855
dscal(int n,double a,double * x)4856 static void dscal(int n, double a, double *x)
4857 {
4858 for (int i = 0; i < n; i++)
4859 x[i] *= a;
4860 }
4861
4862 /*
4863 * The implementation of the solvers is inspired by
4864 * the lsolver library, by Christian Badura, available from:
4865 * http://www.mathematik.uni-freiburg.de
4866 * /IAM/Research/projectskr/lin_solver/
4867 *
4868 * About the Conjugate Gradient, details can be found in:
4869 * Ashby, Manteuffel, Saylor
4870 * A taxononmy for conjugate gradient methods
4871 * SIAM J Numer Anal 27, 1542-1568 (1990)
4872 *
4873 * This version is completely abstract, the same code can be used for
4874 * CPU/GPU, dense matrix / sparse matrix etc...
4875 * Abstraction is realized through:
4876 * - Abstract matrix interface (NLMatrix), that can implement different
4877 * versions of matrix x vector product (CPU/GPU, sparse/dense ...)
4878 */
4879
nlSolveSystem_PRE_CG(NLMatrix M,NLMatrix P,double * b,double * x,double eps,uint32_t max_iter,double * sq_bnorm,double * sq_rnorm)4880 static uint32_t nlSolveSystem_PRE_CG(NLMatrix M, NLMatrix P, double* b, double* x, double eps, uint32_t max_iter, double *sq_bnorm, double *sq_rnorm)
4881 {
4882 int N = (int)M->n;
4883 double* r = NL_NEW_VECTOR(N);
4884 double* d = NL_NEW_VECTOR(N);
4885 double* h = NL_NEW_VECTOR(N);
4886 double *Ad = h;
4887 uint32_t its = 0;
4888 double rh, alpha, beta;
4889 double b_square = ddot(N, b, b);
4890 double err = eps * eps*b_square;
4891 double curr_err;
4892 nlMultMatrixVector(M, x, r);
4893 daxpy(N, -1., b, r);
4894 nlMultMatrixVector(P, r, d);
4895 memcpy(h, d, N * sizeof(double));
4896 rh = ddot(N, r, h);
4897 curr_err = ddot(N, r, r);
4898 while (curr_err > err && its < max_iter) {
4899 nlMultMatrixVector(M, d, Ad);
4900 alpha = rh / ddot(N, d, Ad);
4901 daxpy(N, -alpha, d, x);
4902 daxpy(N, -alpha, Ad, r);
4903 nlMultMatrixVector(P, r, h);
4904 beta = 1. / rh;
4905 rh = ddot(N, r, h);
4906 beta *= rh;
4907 dscal(N, beta, d);
4908 daxpy(N, 1., h, d);
4909 ++its;
4910 curr_err = ddot(N, r, r);
4911 }
4912 NL_DELETE_VECTOR(r);
4913 NL_DELETE_VECTOR(d);
4914 NL_DELETE_VECTOR(h);
4915 *sq_bnorm = b_square;
4916 *sq_rnorm = curr_err;
4917 return its;
4918 }
4919
nlSolveSystemIterative(NLContext * context,NLMatrix M,NLMatrix P,double * b_in,double * x_in,double eps,uint32_t max_iter)4920 static uint32_t nlSolveSystemIterative(NLContext *context, NLMatrix M, NLMatrix P, double* b_in, double* x_in, double eps, uint32_t max_iter)
4921 {
4922 uint32_t result = 0;
4923 double rnorm = 0.0;
4924 double bnorm = 0.0;
4925 double* b = b_in;
4926 double* x = x_in;
4927 XA_DEBUG_ASSERT(M->m == M->n);
4928 double sq_bnorm, sq_rnorm;
4929 result = nlSolveSystem_PRE_CG(M, P, b, x, eps, max_iter, &sq_bnorm, &sq_rnorm);
4930 /* Get residual norm and rhs norm */
4931 bnorm = sqrt(sq_bnorm);
4932 rnorm = sqrt(sq_rnorm);
4933 if (bnorm == 0.0)
4934 context->error = rnorm;
4935 else
4936 context->error = rnorm / bnorm;
4937 context->used_iterations = result;
4938 return result;
4939 }
4940
nlSolveIterative(NLContext * context)4941 static bool nlSolveIterative(NLContext *context)
4942 {
4943 double* b = context->b;
4944 double* x = context->x;
4945 uint32_t n = context->n;
4946 NLMatrix M = context->M;
4947 NLMatrix P = context->P;
4948 for (uint32_t k = 0; k < context->nb_systems; ++k) {
4949 nlSolveSystemIterative(context, M, P, b, x, context->threshold, context->max_iterations);
4950 b += n;
4951 x += n;
4952 }
4953 return true;
4954 }
4955
4956 struct NLJacobiPreconditioner
4957 {
4958 uint32_t m;
4959 uint32_t n;
4960 uint32_t type;
4961 NLDestroyMatrixFunc destroy_func;
4962 NLMultMatrixVectorFunc mult_func;
4963 double* diag_inv;
4964 };
4965
nlJacobiPreconditionerDestroy(NLJacobiPreconditioner * M)4966 static void nlJacobiPreconditionerDestroy(NLJacobiPreconditioner* M)
4967 {
4968 NL_DELETE_ARRAY(M->diag_inv);
4969 }
4970
nlJacobiPreconditionerMult(NLJacobiPreconditioner * M,const double * x,double * y)4971 static void nlJacobiPreconditionerMult(NLJacobiPreconditioner* M, const double* x, double* y)
4972 {
4973 for (uint32_t i = 0; i < M->n; ++i)
4974 y[i] = x[i] * M->diag_inv[i];
4975 }
4976
nlNewJacobiPreconditioner(NLMatrix M_in)4977 static NLMatrix nlNewJacobiPreconditioner(NLMatrix M_in)
4978 {
4979 NLSparseMatrix* M = nullptr;
4980 NLJacobiPreconditioner* result = nullptr;
4981 XA_DEBUG_ASSERT(M_in->type == NL_MATRIX_SPARSE_DYNAMIC);
4982 XA_DEBUG_ASSERT(M_in->m == M_in->n);
4983 M = (NLSparseMatrix*)M_in;
4984 result = NL_NEW(NLJacobiPreconditioner);
4985 NL_CLEAR(result, NLJacobiPreconditioner);
4986 result->m = M->m;
4987 result->n = M->n;
4988 result->type = NL_MATRIX_OTHER;
4989 result->destroy_func = (NLDestroyMatrixFunc)nlJacobiPreconditionerDestroy;
4990 result->mult_func = (NLMultMatrixVectorFunc)nlJacobiPreconditionerMult;
4991 result->diag_inv = NL_NEW_ARRAY(double, M->n);
4992 NL_CLEAR_ARRAY(double, result->diag_inv, M->n);
4993 for (uint32_t i = 0; i < M->n; ++i)
4994 result->diag_inv[i] = (M->diag[i] == 0.0) ? 1.0 : 1.0 / M->diag[i];
4995 return (NLMatrix)result;
4996 }
4997
4998 #define NL_NB_VARIABLES 0x101
4999 #define NL_MAX_ITERATIONS 0x103
5000
nlSolverParameteri(NLContext * context,uint32_t pname,int param)5001 static void nlSolverParameteri(NLContext *context, uint32_t pname, int param)
5002 {
5003 if (pname == NL_NB_VARIABLES) {
5004 XA_DEBUG_ASSERT(param > 0);
5005 context->nb_variables = (uint32_t)param;
5006 } else if (pname == NL_MAX_ITERATIONS) {
5007 XA_DEBUG_ASSERT(param > 0);
5008 context->max_iterations = (uint32_t)param;
5009 context->max_iterations_defined = true;
5010 }
5011 }
5012
nlSetVariable(NLContext * context,uint32_t index,double value)5013 static void nlSetVariable(NLContext *context, uint32_t index, double value)
5014 {
5015 XA_DEBUG_ASSERT(index >= 0 && index <= context->nb_variables - 1);
5016 NL_BUFFER_ITEM(context->variable_buffer[0], index) = value;
5017 }
5018
nlGetVariable(NLContext * context,uint32_t index)5019 static double nlGetVariable(NLContext *context, uint32_t index)
5020 {
5021 XA_DEBUG_ASSERT(index >= 0 && index <= context->nb_variables - 1);
5022 return NL_BUFFER_ITEM(context->variable_buffer[0], index);
5023 }
5024
nlLockVariable(NLContext * context,uint32_t index)5025 static void nlLockVariable(NLContext *context, uint32_t index)
5026 {
5027 XA_DEBUG_ASSERT(index >= 0 && index <= context->nb_variables - 1);
5028 context->variable_is_locked[index] = true;
5029 }
5030
nlVariablesToVector(NLContext * context)5031 static void nlVariablesToVector(NLContext *context)
5032 {
5033 uint32_t n = context->n;
5034 XA_DEBUG_ASSERT(context->x);
5035 for (uint32_t k = 0; k < context->nb_systems; ++k) {
5036 for (uint32_t i = 0; i < context->nb_variables; ++i) {
5037 if (!context->variable_is_locked[i]) {
5038 uint32_t index = context->variable_index[i];
5039 XA_DEBUG_ASSERT(index < context->n);
5040 double value = NL_BUFFER_ITEM(context->variable_buffer[k], i);
5041 context->x[index + k * n] = value;
5042 }
5043 }
5044 }
5045 }
5046
nlVectorToVariables(NLContext * context)5047 static void nlVectorToVariables(NLContext *context)
5048 {
5049 uint32_t n = context->n;
5050 XA_DEBUG_ASSERT(context->x);
5051 for (uint32_t k = 0; k < context->nb_systems; ++k) {
5052 for (uint32_t i = 0; i < context->nb_variables; ++i) {
5053 if (!context->variable_is_locked[i]) {
5054 uint32_t index = context->variable_index[i];
5055 XA_DEBUG_ASSERT(index < context->n);
5056 double value = context->x[index + k * n];
5057 NL_BUFFER_ITEM(context->variable_buffer[k], i) = value;
5058 }
5059 }
5060 }
5061 }
5062
nlCoefficient(NLContext * context,uint32_t index,double value)5063 static void nlCoefficient(NLContext *context, uint32_t index, double value)
5064 {
5065 XA_DEBUG_ASSERT(index >= 0 && index <= context->nb_variables - 1);
5066 if (context->variable_is_locked[index]) {
5067 /*
5068 * Note: in al, indices are NLvariable indices,
5069 * within [0..nb_variables-1]
5070 */
5071 nlRowColumnAppend(&(context->al), index, value);
5072 } else {
5073 /*
5074 * Note: in af, indices are system indices,
5075 * within [0..n-1]
5076 */
5077 nlRowColumnAppend(&(context->af), context->variable_index[index], value);
5078 }
5079 }
5080
5081 #define NL_SYSTEM 0x0
5082 #define NL_MATRIX 0x1
5083 #define NL_ROW 0x2
5084
nlBegin(NLContext * context,uint32_t prim)5085 static void nlBegin(NLContext *context, uint32_t prim)
5086 {
5087 if (prim == NL_SYSTEM) {
5088 XA_DEBUG_ASSERT(context->nb_variables > 0);
5089 context->variable_buffer = NL_NEW_ARRAY(NLBufferBinding, context->nb_systems);
5090 NL_CLEAR_ARRAY(NLBufferBinding, context->variable_buffer, context->nb_systems);
5091 context->variable_value = NL_NEW_ARRAY(double, context->nb_variables * context->nb_systems);
5092 NL_CLEAR_ARRAY(double, context->variable_value, context->nb_variables * context->nb_systems);
5093 for (uint32_t k = 0; k < context->nb_systems; ++k) {
5094 context->variable_buffer[k].base_address =
5095 context->variable_value +
5096 k * context->nb_variables;
5097 context->variable_buffer[k].stride = sizeof(double);
5098 }
5099 context->variable_is_locked = NL_NEW_ARRAY(bool, context->nb_variables);
5100 NL_CLEAR_ARRAY(bool, context->variable_is_locked, context->nb_variables);
5101 context->variable_index = NL_NEW_ARRAY(uint32_t, context->nb_variables);
5102 NL_CLEAR_ARRAY(uint32_t, context->variable_index, context->nb_variables);
5103 } else if (prim == NL_MATRIX) {
5104 if (context->M)
5105 return;
5106 uint32_t n = 0;
5107 for (uint32_t i = 0; i < context->nb_variables; i++) {
5108 if (!context->variable_is_locked[i]) {
5109 context->variable_index[i] = n;
5110 n++;
5111 } else
5112 context->variable_index[i] = (uint32_t)~0;
5113 }
5114 context->n = n;
5115 if (!context->max_iterations_defined)
5116 context->max_iterations = n * 5;
5117 context->M = (NLMatrix)(NL_NEW(NLSparseMatrix));
5118 NL_CLEAR(context->M, NLSparseMatrix);
5119 nlSparseMatrixConstruct((NLSparseMatrix*)(context->M), n, n);
5120 context->x = NL_NEW_ARRAY(double, n*context->nb_systems);
5121 NL_CLEAR_ARRAY(double, context->x, n*context->nb_systems);
5122 context->b = NL_NEW_ARRAY(double, n*context->nb_systems);
5123 NL_CLEAR_ARRAY(double, context->b, n*context->nb_systems);
5124 nlVariablesToVector(context);
5125 nlRowColumnConstruct(&context->af);
5126 nlRowColumnConstruct(&context->al);
5127 context->current_row = 0;
5128 } else if (prim == NL_ROW) {
5129 nlRowColumnZero(&context->af);
5130 nlRowColumnZero(&context->al);
5131 }
5132 }
5133
nlEnd(NLContext * context,uint32_t prim)5134 static void nlEnd(NLContext *context, uint32_t prim)
5135 {
5136 if (prim == NL_MATRIX) {
5137 nlRowColumnClear(&context->af);
5138 nlRowColumnClear(&context->al);
5139 } else if (prim == NL_ROW) {
5140 NLRowColumn* af = &context->af;
5141 NLRowColumn* al = &context->al;
5142 NLSparseMatrix* M = (NLSparseMatrix*)context->M;
5143 double* b = context->b;
5144 uint32_t nf = af->size;
5145 uint32_t nl = al->size;
5146 uint32_t n = context->n;
5147 double S;
5148 /*
5149 * least_squares : we want to solve
5150 * A'A x = A'b
5151 */
5152 for (uint32_t i = 0; i < nf; i++) {
5153 for (uint32_t j = 0; j < nf; j++) {
5154 nlSparseMatrixAdd(M, af->coeff[i].index, af->coeff[j].index, af->coeff[i].value * af->coeff[j].value);
5155 }
5156 }
5157 for (uint32_t k = 0; k < context->nb_systems; ++k) {
5158 S = 0.0;
5159 for (uint32_t jj = 0; jj < nl; ++jj) {
5160 uint32_t j = al->coeff[jj].index;
5161 S += al->coeff[jj].value * NL_BUFFER_ITEM(context->variable_buffer[k], j);
5162 }
5163 for (uint32_t jj = 0; jj < nf; jj++)
5164 b[k*n + af->coeff[jj].index] -= af->coeff[jj].value * S;
5165 }
5166 context->current_row++;
5167 }
5168 }
5169
nlSolve(NLContext * context)5170 static bool nlSolve(NLContext *context)
5171 {
5172 nlDeleteMatrix(context->P);
5173 context->P = nlNewJacobiPreconditioner(context->M);
5174 nlMatrixCompress(&context->M);
5175 bool result = nlSolveIterative(context);
5176 nlVectorToVariables(context);
5177 return result;
5178 }
5179 } // namespace opennl
5180
5181 namespace raster {
5182 class ClippedTriangle
5183 {
5184 public:
ClippedTriangle(const Vector2 & a,const Vector2 & b,const Vector2 & c)5185 ClippedTriangle(const Vector2 &a, const Vector2 &b, const Vector2 &c)
5186 {
5187 m_numVertices = 3;
5188 m_activeVertexBuffer = 0;
5189 m_verticesA[0] = a;
5190 m_verticesA[1] = b;
5191 m_verticesA[2] = c;
5192 m_vertexBuffers[0] = m_verticesA;
5193 m_vertexBuffers[1] = m_verticesB;
5194 m_area = 0;
5195 }
5196
clipHorizontalPlane(float offset,float clipdirection)5197 void clipHorizontalPlane(float offset, float clipdirection)
5198 {
5199 Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
5200 m_activeVertexBuffer ^= 1;
5201 Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer];
5202 v[m_numVertices] = v[0];
5203 float dy2, dy1 = offset - v[0].y;
5204 int dy2in, dy1in = clipdirection * dy1 >= 0;
5205 uint32_t p = 0;
5206 for (uint32_t k = 0; k < m_numVertices; k++) {
5207 dy2 = offset - v[k + 1].y;
5208 dy2in = clipdirection * dy2 >= 0;
5209 if (dy1in) v2[p++] = v[k];
5210 if ( dy1in + dy2in == 1 ) { // not both in/out
5211 float dx = v[k + 1].x - v[k].x;
5212 float dy = v[k + 1].y - v[k].y;
5213 v2[p++] = Vector2(v[k].x + dy1 * (dx / dy), offset);
5214 }
5215 dy1 = dy2;
5216 dy1in = dy2in;
5217 }
5218 m_numVertices = p;
5219 }
5220
clipVerticalPlane(float offset,float clipdirection)5221 void clipVerticalPlane(float offset, float clipdirection)
5222 {
5223 Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
5224 m_activeVertexBuffer ^= 1;
5225 Vector2 *v2 = m_vertexBuffers[m_activeVertexBuffer];
5226 v[m_numVertices] = v[0];
5227 float dx2, dx1 = offset - v[0].x;
5228 int dx2in, dx1in = clipdirection * dx1 >= 0;
5229 uint32_t p = 0;
5230 for (uint32_t k = 0; k < m_numVertices; k++) {
5231 dx2 = offset - v[k + 1].x;
5232 dx2in = clipdirection * dx2 >= 0;
5233 if (dx1in) v2[p++] = v[k];
5234 if ( dx1in + dx2in == 1 ) { // not both in/out
5235 float dx = v[k + 1].x - v[k].x;
5236 float dy = v[k + 1].y - v[k].y;
5237 v2[p++] = Vector2(offset, v[k].y + dx1 * (dy / dx));
5238 }
5239 dx1 = dx2;
5240 dx1in = dx2in;
5241 }
5242 m_numVertices = p;
5243 }
5244
computeArea()5245 void computeArea()
5246 {
5247 Vector2 *v = m_vertexBuffers[m_activeVertexBuffer];
5248 v[m_numVertices] = v[0];
5249 m_area = 0;
5250 float centroidx = 0, centroidy = 0;
5251 for (uint32_t k = 0; k < m_numVertices; k++) {
5252 // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
5253 float f = v[k].x * v[k + 1].y - v[k + 1].x * v[k].y;
5254 m_area += f;
5255 centroidx += f * (v[k].x + v[k + 1].x);
5256 centroidy += f * (v[k].y + v[k + 1].y);
5257 }
5258 m_area = 0.5f * fabsf(m_area);
5259 }
5260
clipAABox(float x0,float y0,float x1,float y1)5261 void clipAABox(float x0, float y0, float x1, float y1)
5262 {
5263 clipVerticalPlane(x0, -1);
5264 clipHorizontalPlane(y0, -1);
5265 clipVerticalPlane(x1, 1);
5266 clipHorizontalPlane(y1, 1);
5267 computeArea();
5268 }
5269
area() const5270 float area() const
5271 {
5272 return m_area;
5273 }
5274
5275 private:
5276 Vector2 m_verticesA[7 + 1];
5277 Vector2 m_verticesB[7 + 1];
5278 Vector2 *m_vertexBuffers[2];
5279 uint32_t m_numVertices;
5280 uint32_t m_activeVertexBuffer;
5281 float m_area;
5282 };
5283
5284 /// A callback to sample the environment. Return false to terminate rasterization.
5285 typedef bool (*SamplingCallback)(void *param, int x, int y);
5286
5287 /// A triangle for rasterization.
5288 struct Triangle
5289 {
Trianglexatlas::internal::raster::Triangle5290 Triangle(const Vector2 &_v0, const Vector2 &_v1, const Vector2 &_v2) : v1(_v0), v2(_v2), v3(_v1)
5291 {
5292 // make sure every triangle is front facing.
5293 flipBackface();
5294 // Compute deltas.
5295 if (isValid())
5296 computeUnitInwardNormals();
5297 }
5298
isValidxatlas::internal::raster::Triangle5299 bool isValid()
5300 {
5301 const Vector2 e0 = v3 - v1;
5302 const Vector2 e1 = v2 - v1;
5303 const float area = e0.y * e1.x - e1.y * e0.x;
5304 return area != 0.0f;
5305 }
5306
5307 // extents has to be multiple of BK_SIZE!!
drawAAxatlas::internal::raster::Triangle5308 bool drawAA(const Vector2 &extents, SamplingCallback cb, void *param)
5309 {
5310 const float PX_INSIDE = 1.0f/sqrtf(2.0f);
5311 const float PX_OUTSIDE = -1.0f/sqrtf(2.0f);
5312 const float BK_SIZE = 8;
5313 const float BK_INSIDE = sqrtf(BK_SIZE*BK_SIZE/2.0f);
5314 const float BK_OUTSIDE = -sqrtf(BK_SIZE*BK_SIZE/2.0f);
5315 // Bounding rectangle
5316 float minx = floorf(max(min3(v1.x, v2.x, v3.x), 0.0f));
5317 float miny = floorf(max(min3(v1.y, v2.y, v3.y), 0.0f));
5318 float maxx = ceilf( min(max3(v1.x, v2.x, v3.x), extents.x - 1.0f));
5319 float maxy = ceilf( min(max3(v1.y, v2.y, v3.y), extents.y - 1.0f));
5320 // There's no reason to align the blocks to the viewport, instead we align them to the origin of the triangle bounds.
5321 minx = floorf(minx);
5322 miny = floorf(miny);
5323 //minx = (float)(((int)minx) & (~((int)BK_SIZE - 1))); // align to blocksize (we don't need to worry about blocks partially out of viewport)
5324 //miny = (float)(((int)miny) & (~((int)BK_SIZE - 1)));
5325 minx += 0.5;
5326 miny += 0.5; // sampling at texel centers!
5327 maxx += 0.5;
5328 maxy += 0.5;
5329 // Half-edge constants
5330 float C1 = n1.x * (-v1.x) + n1.y * (-v1.y);
5331 float C2 = n2.x * (-v2.x) + n2.y * (-v2.y);
5332 float C3 = n3.x * (-v3.x) + n3.y * (-v3.y);
5333 // Loop through blocks
5334 for (float y0 = miny; y0 <= maxy; y0 += BK_SIZE) {
5335 for (float x0 = minx; x0 <= maxx; x0 += BK_SIZE) {
5336 // Corners of block
5337 float xc = (x0 + (BK_SIZE - 1) / 2.0f);
5338 float yc = (y0 + (BK_SIZE - 1) / 2.0f);
5339 // Evaluate half-space functions
5340 float aC = C1 + n1.x * xc + n1.y * yc;
5341 float bC = C2 + n2.x * xc + n2.y * yc;
5342 float cC = C3 + n3.x * xc + n3.y * yc;
5343 // Skip block when outside an edge
5344 if ( (aC <= BK_OUTSIDE) || (bC <= BK_OUTSIDE) || (cC <= BK_OUTSIDE) ) continue;
5345 // Accept whole block when totally covered
5346 if ( (aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE) ) {
5347 for (float y = y0; y < y0 + BK_SIZE; y++) {
5348 for (float x = x0; x < x0 + BK_SIZE; x++) {
5349 if (!cb(param, (int)x, (int)y))
5350 return false;
5351 }
5352 }
5353 } else { // Partially covered block
5354 float CY1 = C1 + n1.x * x0 + n1.y * y0;
5355 float CY2 = C2 + n2.x * x0 + n2.y * y0;
5356 float CY3 = C3 + n3.x * x0 + n3.y * y0;
5357 for (float y = y0; y < y0 + BK_SIZE; y++) { // @@ This is not clipping to scissor rectangle correctly.
5358 float CX1 = CY1;
5359 float CX2 = CY2;
5360 float CX3 = CY3;
5361 for (float x = x0; x < x0 + BK_SIZE; x++) { // @@ This is not clipping to scissor rectangle correctly.
5362 if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE) {
5363 if (!cb(param, (int)x, (int)y))
5364 return false;
5365 } else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE)) {
5366 // triangle partially covers pixel. do clipping.
5367 ClippedTriangle ct(v1 - Vector2(x, y), v2 - Vector2(x, y), v3 - Vector2(x, y));
5368 ct.clipAABox(-0.5, -0.5, 0.5, 0.5);
5369 if (ct.area() > 0.0f) {
5370 if (!cb(param, (int)x, (int)y))
5371 return false;
5372 }
5373 }
5374 CX1 += n1.x;
5375 CX2 += n2.x;
5376 CX3 += n3.x;
5377 }
5378 CY1 += n1.y;
5379 CY2 += n2.y;
5380 CY3 += n3.y;
5381 }
5382 }
5383 }
5384 }
5385 return true;
5386 }
5387
5388 private:
flipBackfacexatlas::internal::raster::Triangle5389 void flipBackface()
5390 {
5391 // check if triangle is backfacing, if so, swap two vertices
5392 if ( ((v3.x - v1.x) * (v2.y - v1.y) - (v3.y - v1.y) * (v2.x - v1.x)) < 0 ) {
5393 Vector2 hv = v1;
5394 v1 = v2;
5395 v2 = hv; // swap pos
5396 }
5397 }
5398
5399 // compute unit inward normals for each edge.
computeUnitInwardNormalsxatlas::internal::raster::Triangle5400 void computeUnitInwardNormals()
5401 {
5402 n1 = v1 - v2;
5403 n1 = Vector2(-n1.y, n1.x);
5404 n1 = n1 * (1.0f / sqrtf(dot(n1, n1)));
5405 n2 = v2 - v3;
5406 n2 = Vector2(-n2.y, n2.x);
5407 n2 = n2 * (1.0f / sqrtf(dot(n2, n2)));
5408 n3 = v3 - v1;
5409 n3 = Vector2(-n3.y, n3.x);
5410 n3 = n3 * (1.0f / sqrtf(dot(n3, n3)));
5411 }
5412
5413 // Vertices.
5414 Vector2 v1, v2, v3;
5415 Vector2 n1, n2, n3; // unit inward normals
5416 };
5417
5418 // Process the given triangle. Returns false if rasterization was interrupted by the callback.
drawTriangle(const Vector2 & extents,const Vector2 v[3],SamplingCallback cb,void * param)5419 static bool drawTriangle(const Vector2 &extents, const Vector2 v[3], SamplingCallback cb, void *param)
5420 {
5421 Triangle tri(v[0], v[1], v[2]);
5422 // @@ It would be nice to have a conservative drawing mode that enlarges the triangle extents by one texel and is able to handle degenerate triangles.
5423 // @@ Maybe the simplest thing to do would be raster triangle edges.
5424 if (tri.isValid())
5425 return tri.drawAA(extents, cb, param);
5426 return true;
5427 }
5428
5429 } // namespace raster
5430
5431 namespace segment {
5432
5433 // - Insertion is o(n)
5434 // - Smallest element goes at the end, so that popping it is o(1).
5435 struct CostQueue
5436 {
CostQueuexatlas::internal::segment::CostQueue5437 CostQueue(uint32_t size = UINT32_MAX) : m_maxSize(size), m_pairs(MemTag::SegmentAtlasChartCandidates) {}
5438
peekCostxatlas::internal::segment::CostQueue5439 float peekCost() const
5440 {
5441 return m_pairs.back().cost;
5442 }
5443
peekFacexatlas::internal::segment::CostQueue5444 uint32_t peekFace() const
5445 {
5446 return m_pairs.back().face;
5447 }
5448
pushxatlas::internal::segment::CostQueue5449 void push(float cost, uint32_t face)
5450 {
5451 const Pair p = { cost, face };
5452 if (m_pairs.isEmpty() || cost < peekCost())
5453 m_pairs.push_back(p);
5454 else {
5455 uint32_t i = 0;
5456 const uint32_t count = m_pairs.size();
5457 for (; i < count; i++) {
5458 if (m_pairs[i].cost < cost)
5459 break;
5460 }
5461 m_pairs.insertAt(i, p);
5462 if (m_pairs.size() > m_maxSize)
5463 m_pairs.removeAt(0);
5464 }
5465 }
5466
popxatlas::internal::segment::CostQueue5467 uint32_t pop()
5468 {
5469 XA_DEBUG_ASSERT(!m_pairs.isEmpty());
5470 uint32_t f = m_pairs.back().face;
5471 m_pairs.pop_back();
5472 return f;
5473 }
5474
clearxatlas::internal::segment::CostQueue5475 XA_INLINE void clear()
5476 {
5477 m_pairs.clear();
5478 }
5479
countxatlas::internal::segment::CostQueue5480 XA_INLINE uint32_t count() const
5481 {
5482 return m_pairs.size();
5483 }
5484
5485 private:
5486 const uint32_t m_maxSize;
5487
5488 struct Pair
5489 {
5490 float cost;
5491 uint32_t face;
5492 };
5493
5494 Array<Pair> m_pairs;
5495 };
5496
5497 struct AtlasData
5498 {
5499 ChartOptions options;
5500 const Mesh *mesh = nullptr;
5501 Array<float> edgeDihedralAngles;
5502 Array<float> edgeLengths;
5503 Array<float> faceAreas;
5504 Array<Vector3> faceNormals;
5505 BitArray isFaceInChart;
5506
AtlasDataxatlas::internal::segment::AtlasData5507 AtlasData() : edgeDihedralAngles(MemTag::SegmentAtlasMeshData), edgeLengths(MemTag::SegmentAtlasMeshData), faceAreas(MemTag::SegmentAtlasMeshData), faceNormals(MemTag::SegmentAtlasMeshData) {}
5508
computexatlas::internal::segment::AtlasData5509 void compute()
5510 {
5511 const uint32_t faceCount = mesh->faceCount();
5512 const uint32_t edgeCount = mesh->edgeCount();
5513 edgeDihedralAngles.resize(edgeCount);
5514 edgeLengths.resize(edgeCount);
5515 faceAreas.resize(faceCount);
5516 faceNormals.resize(faceCount);
5517 isFaceInChart.resize(faceCount);
5518 isFaceInChart.zeroOutMemory();
5519 for (uint32_t f = 0; f < faceCount; f++) {
5520 for (uint32_t i = 0; i < 3; i++) {
5521 const uint32_t edge = f * 3 + i;
5522 const Vector3 &p0 = mesh->position(mesh->vertexAt(meshEdgeIndex0(edge)));
5523 const Vector3 &p1 = mesh->position(mesh->vertexAt(meshEdgeIndex1(edge)));
5524 edgeLengths[edge] = length(p1 - p0);
5525 XA_DEBUG_ASSERT(edgeLengths[edge] > 0.0f);
5526 }
5527 faceAreas[f] = mesh->computeFaceArea(f);
5528 XA_DEBUG_ASSERT(faceAreas[f] > 0.0f);
5529 faceNormals[f] = mesh->computeFaceNormal(f);
5530 }
5531 for (uint32_t face = 0; face < faceCount; face++) {
5532 for (uint32_t i = 0; i < 3; i++) {
5533 const uint32_t edge = face * 3 + i;
5534 const uint32_t oedge = mesh->oppositeEdge(edge);
5535 if (oedge == UINT32_MAX)
5536 edgeDihedralAngles[edge] = FLT_MAX;
5537 else {
5538 const uint32_t oface = meshEdgeFace(oedge);
5539 edgeDihedralAngles[edge] = edgeDihedralAngles[oedge] = dot(faceNormals[face], faceNormals[oface]);
5540 }
5541 }
5542 }
5543 }
5544 };
5545
5546 #if XA_DEBUG_EXPORT_OBJ_PLANAR_REGIONS
5547 static uint32_t s_planarRegionsCurrentRegion;
5548 static uint32_t s_planarRegionsCurrentVertex;
5549 #endif
5550
5551 struct PlanarCharts
5552 {
PlanarChartsxatlas::internal::segment::PlanarCharts5553 PlanarCharts(AtlasData &data) : m_data(data), m_nextRegionFace(MemTag::SegmentAtlasPlanarRegions), m_faceToRegionId(MemTag::SegmentAtlasPlanarRegions) {}
chartBasisxatlas::internal::segment::PlanarCharts5554 const Basis &chartBasis(uint32_t chartIndex) const { return m_chartBasis[chartIndex]; }
chartCountxatlas::internal::segment::PlanarCharts5555 uint32_t chartCount() const { return m_charts.size(); }
5556
chartFacesxatlas::internal::segment::PlanarCharts5557 ConstArrayView<uint32_t> chartFaces(uint32_t chartIndex) const
5558 {
5559 const Chart &chart = m_charts[chartIndex];
5560 return ConstArrayView<uint32_t>(&m_chartFaces[chart.firstFace], chart.faceCount);
5561 }
5562
regionIdFromFacexatlas::internal::segment::PlanarCharts5563 uint32_t regionIdFromFace(uint32_t face) const { return m_faceToRegionId[face]; }
nextRegionFacexatlas::internal::segment::PlanarCharts5564 uint32_t nextRegionFace(uint32_t face) const { return m_nextRegionFace[face]; }
regionAreaxatlas::internal::segment::PlanarCharts5565 float regionArea(uint32_t region) const { return m_regionAreas[region]; }
5566
computexatlas::internal::segment::PlanarCharts5567 void compute()
5568 {
5569 const uint32_t faceCount = m_data.mesh->faceCount();
5570 // Precompute regions of coplanar incident faces.
5571 m_regionFirstFace.clear();
5572 m_nextRegionFace.resize(faceCount);
5573 m_faceToRegionId.resize(faceCount);
5574 for (uint32_t f = 0; f < faceCount; f++) {
5575 m_nextRegionFace[f] = f;
5576 m_faceToRegionId[f] = UINT32_MAX;
5577 }
5578 Array<uint32_t> faceStack;
5579 faceStack.reserve(min(faceCount, 16u));
5580 uint32_t regionCount = 0;
5581 for (uint32_t f = 0; f < faceCount; f++) {
5582 if (m_nextRegionFace[f] != f)
5583 continue; // Already assigned.
5584 faceStack.clear();
5585 faceStack.push_back(f);
5586 for (;;) {
5587 if (faceStack.isEmpty())
5588 break;
5589 const uint32_t face = faceStack.back();
5590 m_faceToRegionId[face] = regionCount;
5591 faceStack.pop_back();
5592 for (Mesh::FaceEdgeIterator it(m_data.mesh, face); !it.isDone(); it.advance()) {
5593 const uint32_t oface = it.oppositeFace();
5594 if (it.isBoundary())
5595 continue;
5596 if (m_nextRegionFace[oface] != oface)
5597 continue; // Already assigned.
5598 if (!equal(dot(m_data.faceNormals[face], m_data.faceNormals[oface]), 1.0f, kEpsilon))
5599 continue; // Not coplanar.
5600 const uint32_t next = m_nextRegionFace[face];
5601 m_nextRegionFace[face] = oface;
5602 m_nextRegionFace[oface] = next;
5603 m_faceToRegionId[oface] = regionCount;
5604 faceStack.push_back(oface);
5605 }
5606 }
5607 m_regionFirstFace.push_back(f);
5608 regionCount++;
5609 }
5610 #if XA_DEBUG_EXPORT_OBJ_PLANAR_REGIONS
5611 static std::mutex s_mutex;
5612 {
5613 std::lock_guard<std::mutex> lock(s_mutex);
5614 FILE *file;
5615 XA_FOPEN(file, "debug_mesh_planar_regions.obj", s_planarRegionsCurrentRegion == 0 ? "w" : "a");
5616 if (file) {
5617 m_data.mesh->writeObjVertices(file);
5618 fprintf(file, "s off\n");
5619 for (uint32_t i = 0; i < regionCount; i++) {
5620 fprintf(file, "o region%u\n", s_planarRegionsCurrentRegion);
5621 for (uint32_t j = 0; j < faceCount; j++) {
5622 if (m_faceToRegionId[j] == i)
5623 m_data.mesh->writeObjFace(file, j, s_planarRegionsCurrentVertex);
5624 }
5625 s_planarRegionsCurrentRegion++;
5626 }
5627 s_planarRegionsCurrentVertex += m_data.mesh->vertexCount();
5628 fclose(file);
5629 }
5630 }
5631 #endif
5632 // Precompute planar region areas.
5633 m_regionAreas.resize(regionCount);
5634 m_regionAreas.zeroOutMemory();
5635 for (uint32_t f = 0; f < faceCount; f++)
5636 m_regionAreas[m_faceToRegionId[f]] += m_data.faceAreas[f];
5637 // Create charts from suitable planar regions.
5638 // The dihedral angle of all boundary edges must be >= 90 degrees.
5639 m_charts.clear();
5640 m_chartFaces.clear();
5641 for (uint32_t region = 0; region < regionCount; region++) {
5642 const uint32_t firstRegionFace = m_regionFirstFace[region];
5643 uint32_t face = firstRegionFace;
5644 bool createChart = true;
5645 do {
5646 for (Mesh::FaceEdgeIterator it(m_data.mesh, face); !it.isDone(); it.advance()) {
5647 if (it.isBoundary())
5648 continue; // Ignore mesh boundary edges.
5649 const uint32_t oface = it.oppositeFace();
5650 if (m_faceToRegionId[oface] == region)
5651 continue; // Ignore internal edges.
5652 const float angle = m_data.edgeDihedralAngles[it.edge()];
5653 if (angle > 0.0f && angle < FLT_MAX) { // FLT_MAX on boundaries.
5654 createChart = false;
5655 break;
5656 }
5657 }
5658 if (!createChart)
5659 break;
5660 face = m_nextRegionFace[face];
5661 }
5662 while (face != firstRegionFace);
5663 // Create a chart.
5664 if (createChart) {
5665 Chart chart;
5666 chart.firstFace = m_chartFaces.size();
5667 chart.faceCount = 0;
5668 face = firstRegionFace;
5669 do {
5670 m_data.isFaceInChart.set(face);
5671 m_chartFaces.push_back(face);
5672 chart.faceCount++;
5673 face = m_nextRegionFace[face];
5674 }
5675 while (face != firstRegionFace);
5676 m_charts.push_back(chart);
5677 }
5678 }
5679 // Compute basis for each chart using the first face normal (all faces have the same normal).
5680 m_chartBasis.resize(m_charts.size());
5681 for (uint32_t c = 0; c < m_charts.size(); c++)
5682 {
5683 const uint32_t face = m_chartFaces[m_charts[c].firstFace];
5684 Basis &basis = m_chartBasis[c];
5685 basis.normal = m_data.faceNormals[face];
5686 basis.tangent = Basis::computeTangent(basis.normal);
5687 basis.bitangent = Basis::computeBitangent(basis.normal, basis.tangent);
5688 }
5689 }
5690
5691 private:
5692 struct Chart
5693 {
5694 uint32_t firstFace, faceCount;
5695 };
5696
5697 AtlasData &m_data;
5698 Array<uint32_t> m_regionFirstFace;
5699 Array<uint32_t> m_nextRegionFace;
5700 Array<uint32_t> m_faceToRegionId;
5701 Array<float> m_regionAreas;
5702 Array<Chart> m_charts;
5703 Array<uint32_t> m_chartFaces;
5704 Array<Basis> m_chartBasis;
5705 };
5706
5707 struct ClusteredCharts
5708 {
ClusteredChartsxatlas::internal::segment::ClusteredCharts5709 ClusteredCharts(AtlasData &data, const PlanarCharts &planarCharts) : m_data(data), m_planarCharts(planarCharts), m_texcoords(MemTag::SegmentAtlasMeshData), m_bestTriangles(10), m_placingSeeds(false) {}
5710
~ClusteredChartsxatlas::internal::segment::ClusteredCharts5711 ~ClusteredCharts()
5712 {
5713 const uint32_t chartCount = m_charts.size();
5714 for (uint32_t i = 0; i < chartCount; i++) {
5715 m_charts[i]->~Chart();
5716 XA_FREE(m_charts[i]);
5717 }
5718 }
5719
chartCountxatlas::internal::segment::ClusteredCharts5720 uint32_t chartCount() const { return m_charts.size(); }
chartFacesxatlas::internal::segment::ClusteredCharts5721 ConstArrayView<uint32_t> chartFaces(uint32_t chartIndex) const { return m_charts[chartIndex]->faces; }
chartBasisxatlas::internal::segment::ClusteredCharts5722 const Basis &chartBasis(uint32_t chartIndex) const { return m_charts[chartIndex]->basis; }
5723
computexatlas::internal::segment::ClusteredCharts5724 void compute()
5725 {
5726 const uint32_t faceCount = m_data.mesh->faceCount();
5727 m_facesLeft = 0;
5728 for (uint32_t i = 0; i < faceCount; i++) {
5729 if (!m_data.isFaceInChart.get(i))
5730 m_facesLeft++;
5731 }
5732 const uint32_t chartCount = m_charts.size();
5733 for (uint32_t i = 0; i < chartCount; i++) {
5734 m_charts[i]->~Chart();
5735 XA_FREE(m_charts[i]);
5736 }
5737 m_charts.clear();
5738 m_faceCharts.resize(faceCount);
5739 m_faceCharts.fill(-1);
5740 m_texcoords.resize(faceCount * 3);
5741 if (m_facesLeft == 0)
5742 return;
5743 // Create initial charts greedely.
5744 placeSeeds(m_data.options.maxCost * 0.5f);
5745 if (m_data.options.maxIterations == 0) {
5746 XA_DEBUG_ASSERT(m_facesLeft == 0);
5747 return;
5748 }
5749 relocateSeeds();
5750 resetCharts();
5751 // Restart process growing charts in parallel.
5752 uint32_t iteration = 0;
5753 for (;;) {
5754 growCharts(m_data.options.maxCost);
5755 // When charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration.
5756 fillHoles(m_data.options.maxCost * 0.5f);
5757 #if XA_MERGE_CHARTS
5758 mergeCharts();
5759 #endif
5760 if (++iteration == m_data.options.maxIterations)
5761 break;
5762 if (!relocateSeeds())
5763 break;
5764 resetCharts();
5765 }
5766 // Make sure no holes are left!
5767 XA_DEBUG_ASSERT(m_facesLeft == 0);
5768 }
5769
5770 private:
5771 struct Chart
5772 {
Chartxatlas::internal::segment::ClusteredCharts::Chart5773 Chart() : faces(MemTag::SegmentAtlasChartFaces) {}
5774
5775 int id = -1;
5776 Basis basis; // Best fit normal.
5777 float area = 0.0f;
5778 float boundaryLength = 0.0f;
5779 Vector3 centroidSum = Vector3(0.0f); // Sum of chart face centroids.
5780 Vector3 centroid = Vector3(0.0f); // Average centroid of chart faces.
5781 Array<uint32_t> faces;
5782 Array<uint32_t> failedPlanarRegions;
5783 CostQueue candidates;
5784 uint32_t seed;
5785 };
5786
placeSeedsxatlas::internal::segment::ClusteredCharts5787 void placeSeeds(float threshold)
5788 {
5789 XA_PROFILE_START(clusteredChartsPlaceSeeds)
5790 m_placingSeeds = true;
5791 // Instead of using a predefiened number of seeds:
5792 // - Add seeds one by one, growing chart until a certain treshold.
5793 // - Undo charts and restart growing process.
5794 // @@ How can we give preference to faces far from sharp features as in the LSCM paper?
5795 // - those points can be found using a simple flood filling algorithm.
5796 // - how do we weight the probabilities?
5797 while (m_facesLeft > 0)
5798 createChart(threshold);
5799 m_placingSeeds = false;
5800 XA_PROFILE_END(clusteredChartsPlaceSeeds)
5801 }
5802
5803 // Returns true if any of the charts can grow more.
growChartsxatlas::internal::segment::ClusteredCharts5804 void growCharts(float threshold)
5805 {
5806 XA_PROFILE_START(clusteredChartsGrow)
5807 for (;;) {
5808 if (m_facesLeft == 0)
5809 break;
5810 // Get the single best candidate out of the chart best candidates.
5811 uint32_t bestFace = UINT32_MAX, bestChart = UINT32_MAX;
5812 float lowestCost = FLT_MAX;
5813 for (uint32_t i = 0; i < m_charts.size(); i++) {
5814 Chart *chart = m_charts[i];
5815 // Get the best candidate from the chart.
5816 // Cleanup any best candidates that have been claimed by another chart.
5817 uint32_t face = UINT32_MAX;
5818 float cost = FLT_MAX;
5819 for (;;) {
5820 if (chart->candidates.count() == 0)
5821 break;
5822 cost = chart->candidates.peekCost();
5823 face = chart->candidates.peekFace();
5824 if (!m_data.isFaceInChart.get(face))
5825 break;
5826 else {
5827 // Face belongs to another chart. Pop from queue so the next best candidate can be retrieved.
5828 chart->candidates.pop();
5829 face = UINT32_MAX;
5830 }
5831 }
5832 if (face == UINT32_MAX)
5833 continue; // No candidates for this chart.
5834 // See if best candidate overall.
5835 if (cost < lowestCost) {
5836 lowestCost = cost;
5837 bestFace = face;
5838 bestChart = i;
5839 }
5840 }
5841 if (bestFace == UINT32_MAX || lowestCost > threshold)
5842 break;
5843 Chart *chart = m_charts[bestChart];
5844 chart->candidates.pop(); // Pop the selected candidate from the queue.
5845 if (!addFaceToChart(chart, bestFace))
5846 chart->failedPlanarRegions.push_back(m_planarCharts.regionIdFromFace(bestFace));
5847 }
5848 XA_PROFILE_END(clusteredChartsGrow)
5849 }
5850
resetChartsxatlas::internal::segment::ClusteredCharts5851 void resetCharts()
5852 {
5853 XA_PROFILE_START(clusteredChartsReset)
5854 const uint32_t faceCount = m_data.mesh->faceCount();
5855 for (uint32_t i = 0; i < faceCount; i++) {
5856 if (m_faceCharts[i] != -1)
5857 m_data.isFaceInChart.unset(i);
5858 m_faceCharts[i] = -1;
5859 }
5860 m_facesLeft = 0;
5861 for (uint32_t i = 0; i < faceCount; i++) {
5862 if (!m_data.isFaceInChart.get(i))
5863 m_facesLeft++;
5864 }
5865 const uint32_t chartCount = m_charts.size();
5866 for (uint32_t i = 0; i < chartCount; i++) {
5867 Chart *chart = m_charts[i];
5868 chart->area = 0.0f;
5869 chart->boundaryLength = 0.0f;
5870 chart->basis.normal = Vector3(0.0f);
5871 chart->basis.tangent = Vector3(0.0f);
5872 chart->basis.bitangent = Vector3(0.0f);
5873 chart->centroidSum = Vector3(0.0f);
5874 chart->centroid = Vector3(0.0f);
5875 chart->faces.clear();
5876 chart->candidates.clear();
5877 chart->failedPlanarRegions.clear();
5878 addFaceToChart(chart, chart->seed);
5879 }
5880 XA_PROFILE_END(clusteredChartsReset)
5881 }
5882
relocateSeedsxatlas::internal::segment::ClusteredCharts5883 bool relocateSeeds()
5884 {
5885 XA_PROFILE_START(clusteredChartsRelocateSeeds)
5886 bool anySeedChanged = false;
5887 const uint32_t chartCount = m_charts.size();
5888 for (uint32_t i = 0; i < chartCount; i++) {
5889 if (relocateSeed(m_charts[i])) {
5890 anySeedChanged = true;
5891 }
5892 }
5893 XA_PROFILE_END(clusteredChartsRelocateSeeds)
5894 return anySeedChanged;
5895 }
5896
fillHolesxatlas::internal::segment::ClusteredCharts5897 void fillHoles(float threshold)
5898 {
5899 XA_PROFILE_START(clusteredChartsFillHoles)
5900 while (m_facesLeft > 0)
5901 createChart(threshold);
5902 XA_PROFILE_END(clusteredChartsFillHoles)
5903 }
5904
5905 #if XA_MERGE_CHARTS
mergeChartsxatlas::internal::segment::ClusteredCharts5906 void mergeCharts()
5907 {
5908 XA_PROFILE_START(clusteredChartsMerge)
5909 const uint32_t chartCount = m_charts.size();
5910 // Merge charts progressively until there's none left to merge.
5911 for (;;) {
5912 bool merged = false;
5913 for (int c = chartCount - 1; c >= 0; c--) {
5914 Chart *chart = m_charts[c];
5915 if (chart == nullptr)
5916 continue;
5917 float externalBoundaryLength = 0.0f;
5918 m_sharedBoundaryLengths.resize(chartCount);
5919 m_sharedBoundaryLengths.zeroOutMemory();
5920 m_sharedBoundaryLengthsNoSeams.resize(chartCount);
5921 m_sharedBoundaryLengthsNoSeams.zeroOutMemory();
5922 m_sharedBoundaryEdgeCountNoSeams.resize(chartCount);
5923 m_sharedBoundaryEdgeCountNoSeams.zeroOutMemory();
5924 const uint32_t faceCount = chart->faces.size();
5925 for (uint32_t i = 0; i < faceCount; i++) {
5926 const uint32_t f = chart->faces[i];
5927 for (Mesh::FaceEdgeIterator it(m_data.mesh, f); !it.isDone(); it.advance()) {
5928 const float l = m_data.edgeLengths[it.edge()];
5929 if (it.isBoundary()) {
5930 externalBoundaryLength += l;
5931 } else {
5932 const int neighborChart = m_faceCharts[it.oppositeFace()];
5933 if (neighborChart == -1)
5934 externalBoundaryLength += l;
5935 else if (m_charts[neighborChart] != chart) {
5936 if ((it.isSeam() && (isNormalSeam(it.edge()) || it.isTextureSeam()))) {
5937 externalBoundaryLength += l;
5938 } else {
5939 m_sharedBoundaryLengths[neighborChart] += l;
5940 }
5941 m_sharedBoundaryLengthsNoSeams[neighborChart] += l;
5942 m_sharedBoundaryEdgeCountNoSeams[neighborChart]++;
5943 }
5944 }
5945 }
5946 }
5947 for (int cc = chartCount - 1; cc >= 0; cc--) {
5948 if (cc == c)
5949 continue;
5950 Chart *chart2 = m_charts[cc];
5951 if (chart2 == nullptr)
5952 continue;
5953 // Must share a boundary.
5954 if (m_sharedBoundaryLengths[cc] <= 0.0f)
5955 continue;
5956 // Compare proxies.
5957 if (dot(chart2->basis.normal, chart->basis.normal) < XA_MERGE_CHARTS_MIN_NORMAL_DEVIATION)
5958 continue;
5959 // Obey max chart area and boundary length.
5960 if (m_data.options.maxChartArea > 0.0f && chart->area + chart2->area > m_data.options.maxChartArea)
5961 continue;
5962 if (m_data.options.maxBoundaryLength > 0.0f && chart->boundaryLength + chart2->boundaryLength - m_sharedBoundaryLengthsNoSeams[cc] > m_data.options.maxBoundaryLength)
5963 continue;
5964 // Merge if chart2 has a single face.
5965 // chart1 must have more than 1 face.
5966 // chart2 area must be <= 10% of chart1 area.
5967 if (m_sharedBoundaryLengthsNoSeams[cc] > 0.0f && chart->faces.size() > 1 && chart2->faces.size() == 1 && chart2->area <= chart->area * 0.1f)
5968 goto merge;
5969 // Merge if chart2 has two faces (probably a quad), and chart1 bounds at least 2 of its edges.
5970 if (chart2->faces.size() == 2 && m_sharedBoundaryEdgeCountNoSeams[cc] >= 2)
5971 goto merge;
5972 // Merge if chart2 is wholely inside chart1, ignoring seams.
5973 if (m_sharedBoundaryLengthsNoSeams[cc] > 0.0f && equal(m_sharedBoundaryLengthsNoSeams[cc], chart2->boundaryLength, kEpsilon))
5974 goto merge;
5975 if (m_sharedBoundaryLengths[cc] > 0.2f * max(0.0f, chart->boundaryLength - externalBoundaryLength) ||
5976 m_sharedBoundaryLengths[cc] > 0.75f * chart2->boundaryLength)
5977 goto merge;
5978 continue;
5979 merge:
5980 if (!mergeChart(chart, chart2, m_sharedBoundaryLengthsNoSeams[cc]))
5981 continue;
5982 merged = true;
5983 break;
5984 }
5985 if (merged)
5986 break;
5987 }
5988 if (!merged)
5989 break;
5990 }
5991 // Remove deleted charts.
5992 for (int c = 0; c < int32_t(m_charts.size()); /*do not increment if removed*/) {
5993 if (m_charts[c] == nullptr) {
5994 m_charts.removeAt(c);
5995 // Update m_faceCharts.
5996 const uint32_t faceCount = m_faceCharts.size();
5997 for (uint32_t i = 0; i < faceCount; i++) {
5998 XA_DEBUG_ASSERT(m_faceCharts[i] != c);
5999 XA_DEBUG_ASSERT(m_faceCharts[i] <= int32_t(m_charts.size()));
6000 if (m_faceCharts[i] > c) {
6001 m_faceCharts[i]--;
6002 }
6003 }
6004 } else {
6005 m_charts[c]->id = c;
6006 c++;
6007 }
6008 }
6009 XA_PROFILE_END(clusteredChartsMerge)
6010 }
6011 #endif
6012
6013 private:
createChartxatlas::internal::segment::ClusteredCharts6014 void createChart(float threshold)
6015 {
6016 Chart *chart = XA_NEW(MemTag::Default, Chart);
6017 chart->id = (int)m_charts.size();
6018 m_charts.push_back(chart);
6019 // Pick a face not used by any chart yet, belonging to the largest planar region.
6020 chart->seed = 0;
6021 float largestArea = 0.0f;
6022 for (uint32_t f = 0; f < m_data.mesh->faceCount(); f++) {
6023 if (m_data.isFaceInChart.get(f))
6024 continue;
6025 const float area = m_planarCharts.regionArea(m_planarCharts.regionIdFromFace(f));
6026 if (area > largestArea) {
6027 largestArea = area;
6028 chart->seed = f;
6029 }
6030 }
6031 addFaceToChart(chart, chart->seed);
6032 // Grow the chart as much as possible within the given threshold.
6033 for (;;) {
6034 if (chart->candidates.count() == 0 || chart->candidates.peekCost() > threshold)
6035 break;
6036 const uint32_t f = chart->candidates.pop();
6037 if (m_data.isFaceInChart.get(f))
6038 continue;
6039 if (!addFaceToChart(chart, f)) {
6040 chart->failedPlanarRegions.push_back(m_planarCharts.regionIdFromFace(f));
6041 continue;
6042 }
6043 }
6044 }
6045
isChartBoundaryEdgexatlas::internal::segment::ClusteredCharts6046 bool isChartBoundaryEdge(const Chart *chart, uint32_t edge) const
6047 {
6048 const uint32_t oppositeEdge = m_data.mesh->oppositeEdge(edge);
6049 const uint32_t oppositeFace = meshEdgeFace(oppositeEdge);
6050 return oppositeEdge == UINT32_MAX || m_faceCharts[oppositeFace] != chart->id;
6051 }
6052
computeChartBasisxatlas::internal::segment::ClusteredCharts6053 bool computeChartBasis(Chart *chart, Basis *basis)
6054 {
6055 const uint32_t faceCount = chart->faces.size();
6056 m_tempPoints.resize(chart->faces.size() * 3);
6057 for (uint32_t i = 0; i < faceCount; i++) {
6058 const uint32_t f = chart->faces[i];
6059 for (uint32_t j = 0; j < 3; j++)
6060 m_tempPoints[i * 3 + j] = m_data.mesh->position(m_data.mesh->vertexAt(f * 3 + j));
6061 }
6062 return Fit::computeBasis(m_tempPoints.data(), m_tempPoints.size(), basis);
6063 }
6064
isFaceFlippedxatlas::internal::segment::ClusteredCharts6065 bool isFaceFlipped(uint32_t face) const
6066 {
6067 const Vector2 &v1 = m_texcoords[face * 3 + 0];
6068 const Vector2 &v2 = m_texcoords[face * 3 + 1];
6069 const Vector2 &v3 = m_texcoords[face * 3 + 2];
6070 const float parametricArea = ((v2.x - v1.x) * (v3.y - v1.y) - (v3.x - v1.x) * (v2.y - v1.y)) * 0.5f;
6071 return parametricArea < 0.0f;
6072 }
6073
parameterizeChartxatlas::internal::segment::ClusteredCharts6074 void parameterizeChart(const Chart *chart)
6075 {
6076 const uint32_t faceCount = chart->faces.size();
6077 for (uint32_t i = 0; i < faceCount; i++) {
6078 const uint32_t face = chart->faces[i];
6079 for (uint32_t j = 0; j < 3; j++) {
6080 const uint32_t offset = face * 3 + j;
6081 const Vector3 &pos = m_data.mesh->position(m_data.mesh->vertexAt(offset));
6082 m_texcoords[offset] = Vector2(dot(chart->basis.tangent, pos), dot(chart->basis.bitangent, pos));
6083 }
6084 }
6085 }
6086
6087 // m_faceCharts for the chart faces must be set to the chart ID. Needed to compute boundary edges.
isChartParameterizationValidxatlas::internal::segment::ClusteredCharts6088 bool isChartParameterizationValid(const Chart *chart)
6089 {
6090 const uint32_t faceCount = chart->faces.size();
6091 // Check for flipped faces in the parameterization. OK if all are flipped.
6092 uint32_t flippedFaceCount = 0;
6093 for (uint32_t i = 0; i < faceCount; i++) {
6094 if (isFaceFlipped(chart->faces[i]))
6095 flippedFaceCount++;
6096 }
6097 if (flippedFaceCount != 0 && flippedFaceCount != faceCount)
6098 return false;
6099 // Check for boundary intersection in the parameterization.
6100 XA_PROFILE_START(clusteredChartsPlaceSeedsBoundaryIntersection)
6101 XA_PROFILE_START(clusteredChartsGrowBoundaryIntersection)
6102 m_boundaryGrid.reset(m_texcoords.data());
6103 for (uint32_t i = 0; i < faceCount; i++) {
6104 const uint32_t f = chart->faces[i];
6105 for (uint32_t j = 0; j < 3; j++) {
6106 const uint32_t edge = f * 3 + j;
6107 if (isChartBoundaryEdge(chart, edge))
6108 m_boundaryGrid.append(edge);
6109 }
6110 }
6111 const bool intersection = m_boundaryGrid.intersect(m_data.mesh->epsilon());
6112 #if XA_PROFILE
6113 if (m_placingSeeds)
6114 XA_PROFILE_END(clusteredChartsPlaceSeedsBoundaryIntersection)
6115 else
6116 XA_PROFILE_END(clusteredChartsGrowBoundaryIntersection)
6117 #endif
6118 if (intersection)
6119 return false;
6120 return true;
6121 }
6122
addFaceToChartxatlas::internal::segment::ClusteredCharts6123 bool addFaceToChart(Chart *chart, uint32_t face)
6124 {
6125 XA_DEBUG_ASSERT(!m_data.isFaceInChart.get(face));
6126 const uint32_t oldFaceCount = chart->faces.size();
6127 const bool firstFace = oldFaceCount == 0;
6128 // Append the face and any coplanar connected faces to the chart faces array.
6129 chart->faces.push_back(face);
6130 uint32_t coplanarFace = m_planarCharts.nextRegionFace(face);
6131 while (coplanarFace != face) {
6132 XA_DEBUG_ASSERT(!m_data.isFaceInChart.get(coplanarFace));
6133 chart->faces.push_back(coplanarFace);
6134 coplanarFace = m_planarCharts.nextRegionFace(coplanarFace);
6135 }
6136 const uint32_t faceCount = chart->faces.size();
6137 // Compute basis.
6138 Basis basis;
6139 if (firstFace) {
6140 // Use the first face normal.
6141 // Use any edge as the tangent vector.
6142 basis.normal = m_data.faceNormals[face];
6143 basis.tangent = normalize(m_data.mesh->position(m_data.mesh->vertexAt(face * 3 + 0)) - m_data.mesh->position(m_data.mesh->vertexAt(face * 3 + 1)), kEpsilon);
6144 basis.bitangent = cross(basis.normal, basis.tangent);
6145 } else {
6146 // Use best fit normal.
6147 if (!computeChartBasis(chart, &basis)) {
6148 chart->faces.resize(oldFaceCount);
6149 return false;
6150 }
6151 if (dot(basis.normal, m_data.faceNormals[face]) < 0.0f) // Flip normal if oriented in the wrong direction.
6152 basis.normal = -basis.normal;
6153 }
6154 if (!firstFace) {
6155 // Compute orthogonal parameterization and check that it is valid.
6156 parameterizeChart(chart);
6157 for (uint32_t i = oldFaceCount; i < faceCount; i++)
6158 m_faceCharts[chart->faces[i]] = chart->id;
6159 if (!isChartParameterizationValid(chart)) {
6160 for (uint32_t i = oldFaceCount; i < faceCount; i++)
6161 m_faceCharts[chart->faces[i]] = -1;
6162 chart->faces.resize(oldFaceCount);
6163 return false;
6164 }
6165 }
6166 // Add face(s) to chart.
6167 chart->basis = basis;
6168 chart->area = computeArea(chart, face);
6169 chart->boundaryLength = computeBoundaryLength(chart, face);
6170 for (uint32_t i = oldFaceCount; i < faceCount; i++) {
6171 const uint32_t f = chart->faces[i];
6172 m_faceCharts[f] = chart->id;
6173 m_facesLeft--;
6174 m_data.isFaceInChart.set(f);
6175 chart->centroidSum += m_data.mesh->computeFaceCenter(f);
6176 }
6177 chart->centroid = chart->centroidSum / float(chart->faces.size());
6178 // Refresh candidates.
6179 chart->candidates.clear();
6180 for (uint32_t i = 0; i < faceCount; i++) {
6181 // Traverse neighboring faces, add the ones that do not belong to any chart yet.
6182 const uint32_t f = chart->faces[i];
6183 for (uint32_t j = 0; j < 3; j++) {
6184 const uint32_t edge = f * 3 + j;
6185 const uint32_t oedge = m_data.mesh->oppositeEdge(edge);
6186 if (oedge == UINT32_MAX)
6187 continue; // Boundary edge.
6188 const uint32_t oface = meshEdgeFace(oedge);
6189 if (m_data.isFaceInChart.get(oface))
6190 continue; // Face belongs to another chart.
6191 if (chart->failedPlanarRegions.contains(m_planarCharts.regionIdFromFace(oface)))
6192 continue; // Failed to add this faces planar region to the chart before.
6193 const float cost = computeCost(chart, oface);
6194 if (cost < FLT_MAX)
6195 chart->candidates.push(cost, oface);
6196 }
6197 }
6198 return true;
6199 }
6200
6201 // Returns true if the seed has changed.
relocateSeedxatlas::internal::segment::ClusteredCharts6202 bool relocateSeed(Chart *chart)
6203 {
6204 // Find the first N triangles that fit the proxy best.
6205 const uint32_t faceCount = chart->faces.size();
6206 m_bestTriangles.clear();
6207 for (uint32_t i = 0; i < faceCount; i++) {
6208 const float cost = computeNormalDeviationMetric(chart, chart->faces[i]);
6209 m_bestTriangles.push(cost, chart->faces[i]);
6210 }
6211 // Of those, choose the most central triangle.
6212 uint32_t mostCentral = 0;
6213 float minDistance = FLT_MAX;
6214 for (;;) {
6215 if (m_bestTriangles.count() == 0)
6216 break;
6217 const uint32_t face = m_bestTriangles.pop();
6218 Vector3 faceCentroid = m_data.mesh->computeFaceCenter(face);
6219 const float distance = length(chart->centroid - faceCentroid);
6220 if (distance < minDistance) {
6221 minDistance = distance;
6222 mostCentral = face;
6223 }
6224 }
6225 XA_DEBUG_ASSERT(minDistance < FLT_MAX);
6226 if (mostCentral == chart->seed)
6227 return false;
6228 chart->seed = mostCentral;
6229 return true;
6230 }
6231
6232 // Cost is combined metrics * weights.
computeCostxatlas::internal::segment::ClusteredCharts6233 float computeCost(Chart *chart, uint32_t face) const
6234 {
6235 // Estimate boundary length and area:
6236 const float newChartArea = computeArea(chart, face);
6237 const float newBoundaryLength = computeBoundaryLength(chart, face);
6238 // Enforce limits strictly:
6239 if (m_data.options.maxChartArea > 0.0f && newChartArea > m_data.options.maxChartArea)
6240 return FLT_MAX;
6241 if (m_data.options.maxBoundaryLength > 0.0f && newBoundaryLength > m_data.options.maxBoundaryLength)
6242 return FLT_MAX;
6243 // Compute metrics.
6244 float cost = 0.0f;
6245 const float normalDeviation = computeNormalDeviationMetric(chart, face);
6246 if (normalDeviation >= 0.707f) // ~75 degrees
6247 return FLT_MAX;
6248 cost += m_data.options.normalDeviationWeight * normalDeviation;
6249 // Penalize faces that cross seams, reward faces that close seams or reach boundaries.
6250 // Make sure normal seams are fully respected:
6251 const float normalSeam = computeNormalSeamMetric(chart, face);
6252 if (m_data.options.normalSeamWeight >= 1000.0f && normalSeam > 0.0f)
6253 return FLT_MAX;
6254 cost += m_data.options.normalSeamWeight * normalSeam;
6255 cost += m_data.options.roundnessWeight * computeRoundnessMetric(chart, newBoundaryLength, newChartArea);
6256 cost += m_data.options.straightnessWeight * computeStraightnessMetric(chart, face);
6257 cost += m_data.options.textureSeamWeight * computeTextureSeamMetric(chart, face);
6258 //float R = evaluateCompletenessMetric(chart, face);
6259 //float D = evaluateDihedralAngleMetric(chart, face);
6260 // @@ Add a metric based on local dihedral angle.
6261 // @@ Tweaking the normal and texture seam metrics.
6262 // - Cause more impedance. Never cross 90 degree edges.
6263 XA_DEBUG_ASSERT(isFinite(cost));
6264 return cost;
6265 }
6266
6267 // Returns a value in [0-1].
6268 // 0 if face normal is coplanar to the chart's best fit normal.
6269 // 1 if face normal is perpendicular.
computeNormalDeviationMetricxatlas::internal::segment::ClusteredCharts6270 float computeNormalDeviationMetric(Chart *chart, uint32_t face) const
6271 {
6272 // All faces in coplanar regions have the same normal, can use any face.
6273 const Vector3 faceNormal = m_data.faceNormals[face];
6274 // Use plane fitting metric for now:
6275 return min(1.0f - dot(faceNormal, chart->basis.normal), 1.0f); // @@ normal deviations should be weighted by face area
6276 }
6277
computeRoundnessMetricxatlas::internal::segment::ClusteredCharts6278 float computeRoundnessMetric(Chart *chart, float newBoundaryLength, float newChartArea) const
6279 {
6280 const float oldRoundness = square(chart->boundaryLength) / chart->area;
6281 const float newRoundness = square(newBoundaryLength) / newChartArea;
6282 return 1.0f - oldRoundness / newRoundness;
6283 }
6284
computeStraightnessMetricxatlas::internal::segment::ClusteredCharts6285 float computeStraightnessMetric(Chart *chart, uint32_t firstFace) const
6286 {
6287 float l_out = 0.0f; // Length of firstFace planar region boundary that doesn't border the chart.
6288 float l_in = 0.0f; // Length that does border the chart.
6289 const uint32_t planarRegionId = m_planarCharts.regionIdFromFace(firstFace);
6290 uint32_t face = firstFace;
6291 for (;;) {
6292 for (Mesh::FaceEdgeIterator it(m_data.mesh, face); !it.isDone(); it.advance()) {
6293 const float l = m_data.edgeLengths[it.edge()];
6294 if (it.isBoundary()) {
6295 l_out += l;
6296 } else if (m_planarCharts.regionIdFromFace(it.oppositeFace()) != planarRegionId) {
6297 if (m_faceCharts[it.oppositeFace()] != chart->id)
6298 l_out += l;
6299 else
6300 l_in += l;
6301 }
6302 }
6303 face = m_planarCharts.nextRegionFace(face);
6304 if (face == firstFace)
6305 break;
6306 }
6307 #if 1
6308 XA_DEBUG_ASSERT(l_in != 0.0f); // Candidate face must be adjacent to chart. @@ This is not true if the input mesh has zero-length edges.
6309 float ratio = (l_out - l_in) / (l_out + l_in);
6310 return min(ratio, 0.0f); // Only use the straightness metric to close gaps.
6311 #else
6312 return 1.0f - l_in / l_out;
6313 #endif
6314 }
6315
isNormalSeamxatlas::internal::segment::ClusteredCharts6316 bool isNormalSeam(uint32_t edge) const
6317 {
6318 const uint32_t oppositeEdge = m_data.mesh->oppositeEdge(edge);
6319 if (oppositeEdge == UINT32_MAX)
6320 return false; // boundary edge
6321 if (m_data.mesh->flags() & MeshFlags::HasNormals) {
6322 const uint32_t v0 = m_data.mesh->vertexAt(meshEdgeIndex0(edge));
6323 const uint32_t v1 = m_data.mesh->vertexAt(meshEdgeIndex1(edge));
6324 const uint32_t ov0 = m_data.mesh->vertexAt(meshEdgeIndex0(oppositeEdge));
6325 const uint32_t ov1 = m_data.mesh->vertexAt(meshEdgeIndex1(oppositeEdge));
6326 if (v0 == ov1 && v1 == ov0)
6327 return false;
6328 return !equal(m_data.mesh->normal(v0), m_data.mesh->normal(ov1), kNormalEpsilon) || !equal(m_data.mesh->normal(v1), m_data.mesh->normal(ov0), kNormalEpsilon);
6329 }
6330 const uint32_t f0 = meshEdgeFace(edge);
6331 const uint32_t f1 = meshEdgeFace(oppositeEdge);
6332 if (m_planarCharts.regionIdFromFace(f0) == m_planarCharts.regionIdFromFace(f1))
6333 return false;
6334 return !equal(m_data.faceNormals[f0], m_data.faceNormals[f1], kNormalEpsilon);
6335 }
6336
computeNormalSeamMetricxatlas::internal::segment::ClusteredCharts6337 float computeNormalSeamMetric(Chart *chart, uint32_t firstFace) const
6338 {
6339 float seamFactor = 0.0f, totalLength = 0.0f;
6340 uint32_t face = firstFace;
6341 for (;;) {
6342 for (Mesh::FaceEdgeIterator it(m_data.mesh, face); !it.isDone(); it.advance()) {
6343 if (it.isBoundary())
6344 continue;
6345 if (m_faceCharts[it.oppositeFace()] != chart->id)
6346 continue;
6347 float l = m_data.edgeLengths[it.edge()];
6348 totalLength += l;
6349 if (!it.isSeam())
6350 continue;
6351 // Make sure it's a normal seam.
6352 if (isNormalSeam(it.edge())) {
6353 float d;
6354 if (m_data.mesh->flags() & MeshFlags::HasNormals) {
6355 const Vector3 &n0 = m_data.mesh->normal(it.vertex0());
6356 const Vector3 &n1 = m_data.mesh->normal(it.vertex1());
6357 const Vector3 &on0 = m_data.mesh->normal(m_data.mesh->vertexAt(meshEdgeIndex0(it.oppositeEdge())));
6358 const Vector3 &on1 = m_data.mesh->normal(m_data.mesh->vertexAt(meshEdgeIndex1(it.oppositeEdge())));
6359 const float d0 = clamp(dot(n0, on1), 0.0f, 1.0f);
6360 const float d1 = clamp(dot(n1, on0), 0.0f, 1.0f);
6361 d = (d0 + d1) * 0.5f;
6362 } else {
6363 d = clamp(dot(m_data.faceNormals[face], m_data.faceNormals[meshEdgeFace(it.oppositeEdge())]), 0.0f, 1.0f);
6364 }
6365 l *= 1 - d;
6366 seamFactor += l;
6367 }
6368 }
6369 face = m_planarCharts.nextRegionFace(face);
6370 if (face == firstFace)
6371 break;
6372 }
6373 if (seamFactor <= 0.0f)
6374 return 0.0f;
6375 return seamFactor / totalLength;
6376 }
6377
computeTextureSeamMetricxatlas::internal::segment::ClusteredCharts6378 float computeTextureSeamMetric(Chart *chart, uint32_t firstFace) const
6379 {
6380 float seamLength = 0.0f, totalLength = 0.0f;
6381 uint32_t face = firstFace;
6382 for (;;) {
6383 for (Mesh::FaceEdgeIterator it(m_data.mesh, face); !it.isDone(); it.advance()) {
6384 if (it.isBoundary())
6385 continue;
6386 if (m_faceCharts[it.oppositeFace()] != chart->id)
6387 continue;
6388 float l = m_data.edgeLengths[it.edge()];
6389 totalLength += l;
6390 if (!it.isSeam())
6391 continue;
6392 // Make sure it's a texture seam.
6393 if (it.isTextureSeam())
6394 seamLength += l;
6395 }
6396 face = m_planarCharts.nextRegionFace(face);
6397 if (face == firstFace)
6398 break;
6399 }
6400 if (seamLength <= 0.0f)
6401 return 0.0f; // Avoid division by zero.
6402 return seamLength / totalLength;
6403 }
6404
computeAreaxatlas::internal::segment::ClusteredCharts6405 float computeArea(Chart *chart, uint32_t firstFace) const
6406 {
6407 float area = chart->area;
6408 uint32_t face = firstFace;
6409 for (;;) {
6410 area += m_data.faceAreas[face];
6411 face = m_planarCharts.nextRegionFace(face);
6412 if (face == firstFace)
6413 break;
6414 }
6415 return area;
6416 }
6417
computeBoundaryLengthxatlas::internal::segment::ClusteredCharts6418 float computeBoundaryLength(Chart *chart, uint32_t firstFace) const
6419 {
6420 float boundaryLength = chart->boundaryLength;
6421 // Add new edges, subtract edges shared with the chart.
6422 const uint32_t planarRegionId = m_planarCharts.regionIdFromFace(firstFace);
6423 uint32_t face = firstFace;
6424 for (;;) {
6425 for (Mesh::FaceEdgeIterator it(m_data.mesh, face); !it.isDone(); it.advance()) {
6426 const float edgeLength = m_data.edgeLengths[it.edge()];
6427 if (it.isBoundary()) {
6428 boundaryLength += edgeLength;
6429 } else if (m_planarCharts.regionIdFromFace(it.oppositeFace()) != planarRegionId) {
6430 if (m_faceCharts[it.oppositeFace()] != chart->id)
6431 boundaryLength += edgeLength;
6432 else
6433 boundaryLength -= edgeLength;
6434 }
6435 }
6436 face = m_planarCharts.nextRegionFace(face);
6437 if (face == firstFace)
6438 break;
6439 }
6440 return max(0.0f, boundaryLength); // @@ Hack!
6441 }
6442
mergeChartxatlas::internal::segment::ClusteredCharts6443 bool mergeChart(Chart *owner, Chart *chart, float sharedBoundaryLength)
6444 {
6445 const uint32_t oldOwnerFaceCount = owner->faces.size();
6446 const uint32_t chartFaceCount = chart->faces.size();
6447 owner->faces.push_back(chart->faces);
6448 for (uint32_t i = 0; i < chartFaceCount; i++) {
6449 XA_DEBUG_ASSERT(m_faceCharts[chart->faces[i]] == chart->id);
6450 m_faceCharts[chart->faces[i]] = owner->id;
6451 }
6452 // Compute basis using best fit normal.
6453 Basis basis;
6454 if (!computeChartBasis(owner, &basis)) {
6455 owner->faces.resize(oldOwnerFaceCount);
6456 for (uint32_t i = 0; i < chartFaceCount; i++)
6457 m_faceCharts[chart->faces[i]] = chart->id;
6458 return false;
6459 }
6460 if (dot(basis.normal, m_data.faceNormals[owner->faces[0]]) < 0.0f) // Flip normal if oriented in the wrong direction.
6461 basis.normal = -basis.normal;
6462 // Compute orthogonal parameterization and check that it is valid.
6463 parameterizeChart(owner);
6464 if (!isChartParameterizationValid(owner)) {
6465 owner->faces.resize(oldOwnerFaceCount);
6466 for (uint32_t i = 0; i < chartFaceCount; i++)
6467 m_faceCharts[chart->faces[i]] = chart->id;
6468 return false;
6469 }
6470 // Merge chart.
6471 owner->basis = basis;
6472 owner->failedPlanarRegions.push_back(chart->failedPlanarRegions);
6473 // Update adjacencies?
6474 owner->area += chart->area;
6475 owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength;
6476 // Delete chart.
6477 m_charts[chart->id] = nullptr;
6478 chart->~Chart();
6479 XA_FREE(chart);
6480 return true;
6481 }
6482
6483 private:
6484 AtlasData &m_data;
6485 const PlanarCharts &m_planarCharts;
6486 Array<Vector2> m_texcoords;
6487 uint32_t m_facesLeft;
6488 Array<int> m_faceCharts;
6489 Array<Chart *> m_charts;
6490 CostQueue m_bestTriangles;
6491 Array<Vector3> m_tempPoints;
6492 UniformGrid2 m_boundaryGrid;
6493 #if XA_MERGE_CHARTS
6494 // mergeCharts
6495 Array<float> m_sharedBoundaryLengths;
6496 Array<float> m_sharedBoundaryLengthsNoSeams;
6497 Array<uint32_t> m_sharedBoundaryEdgeCountNoSeams;
6498 #endif
6499 bool m_placingSeeds;
6500 };
6501
6502 struct Atlas
6503 {
Atlasxatlas::internal::segment::Atlas6504 Atlas() : m_planarCharts(m_data), m_clusteredCharts(m_data, m_planarCharts) {}
6505
chartCountxatlas::internal::segment::Atlas6506 uint32_t chartCount() const
6507 {
6508 return m_planarCharts.chartCount() + m_clusteredCharts.chartCount();
6509 }
6510
chartFacesxatlas::internal::segment::Atlas6511 ConstArrayView<uint32_t> chartFaces(uint32_t chartIndex) const
6512 {
6513 if (chartIndex < m_planarCharts.chartCount())
6514 return m_planarCharts.chartFaces(chartIndex);
6515 chartIndex -= m_planarCharts.chartCount();
6516 return m_clusteredCharts.chartFaces(chartIndex);
6517 }
6518
chartBasisxatlas::internal::segment::Atlas6519 const Basis &chartBasis(uint32_t chartIndex) const
6520 {
6521 if (chartIndex < m_planarCharts.chartCount())
6522 return m_planarCharts.chartBasis(chartIndex);
6523 chartIndex -= m_planarCharts.chartCount();
6524 return m_clusteredCharts.chartBasis(chartIndex);
6525 }
6526
resetxatlas::internal::segment::Atlas6527 void reset(const Mesh *mesh, const ChartOptions &options)
6528 {
6529 XA_PROFILE_START(buildAtlasInit)
6530 m_data.options = options;
6531 m_data.mesh = mesh;
6532 m_data.compute();
6533 XA_PROFILE_END(buildAtlasInit)
6534 }
6535
computexatlas::internal::segment::Atlas6536 void compute()
6537 {
6538 XA_PROFILE_START(planarCharts)
6539 m_planarCharts.compute();
6540 XA_PROFILE_END(planarCharts)
6541 XA_PROFILE_START(clusteredCharts)
6542 m_clusteredCharts.compute();
6543 XA_PROFILE_END(clusteredCharts)
6544 }
6545
6546 private:
6547 AtlasData m_data;
6548 PlanarCharts m_planarCharts;
6549 ClusteredCharts m_clusteredCharts;
6550 };
6551
6552 } // namespace segment
6553
6554 namespace param {
6555
6556 // Fast sweep in 3 directions
findApproximateDiameterVertices(Mesh * mesh,uint32_t * a,uint32_t * b)6557 static bool findApproximateDiameterVertices(Mesh *mesh, uint32_t *a, uint32_t *b)
6558 {
6559 XA_DEBUG_ASSERT(a != nullptr);
6560 XA_DEBUG_ASSERT(b != nullptr);
6561 const uint32_t vertexCount = mesh->vertexCount();
6562 uint32_t minVertex[3];
6563 uint32_t maxVertex[3];
6564 minVertex[0] = minVertex[1] = minVertex[2] = UINT32_MAX;
6565 maxVertex[0] = maxVertex[1] = maxVertex[2] = UINT32_MAX;
6566 for (uint32_t v = 1; v < vertexCount; v++) {
6567 if (mesh->isBoundaryVertex(v)) {
6568 minVertex[0] = minVertex[1] = minVertex[2] = v;
6569 maxVertex[0] = maxVertex[1] = maxVertex[2] = v;
6570 break;
6571 }
6572 }
6573 if (minVertex[0] == UINT32_MAX) {
6574 // Input mesh has not boundaries.
6575 return false;
6576 }
6577 for (uint32_t v = 1; v < vertexCount; v++) {
6578 if (!mesh->isBoundaryVertex(v)) {
6579 // Skip interior vertices.
6580 continue;
6581 }
6582 const Vector3 &pos = mesh->position(v);
6583 if (pos.x < mesh->position(minVertex[0]).x)
6584 minVertex[0] = v;
6585 else if (pos.x > mesh->position(maxVertex[0]).x)
6586 maxVertex[0] = v;
6587 if (pos.y < mesh->position(minVertex[1]).y)
6588 minVertex[1] = v;
6589 else if (pos.y > mesh->position(maxVertex[1]).y)
6590 maxVertex[1] = v;
6591 if (pos.z < mesh->position(minVertex[2]).z)
6592 minVertex[2] = v;
6593 else if (pos.z > mesh->position(maxVertex[2]).z)
6594 maxVertex[2] = v;
6595 }
6596 float lengths[3];
6597 for (int i = 0; i < 3; i++) {
6598 lengths[i] = length(mesh->position(minVertex[i]) - mesh->position(maxVertex[i]));
6599 }
6600 if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) {
6601 *a = minVertex[0];
6602 *b = maxVertex[0];
6603 } else if (lengths[1] > lengths[2]) {
6604 *a = minVertex[1];
6605 *b = maxVertex[1];
6606 } else {
6607 *a = minVertex[2];
6608 *b = maxVertex[2];
6609 }
6610 return true;
6611 }
6612
6613 // From OpenNL LSCM example.
6614 // Computes the coordinates of the vertices of a triangle in a local 2D orthonormal basis of the triangle's plane.
projectTriangle(Vector3 p0,Vector3 p1,Vector3 p2,Vector2 * z0,Vector2 * z1,Vector2 * z2,float epsilon)6615 static void projectTriangle(Vector3 p0, Vector3 p1, Vector3 p2, Vector2 *z0, Vector2 *z1, Vector2 *z2, float epsilon)
6616 {
6617 Vector3 X = normalize(p1 - p0, epsilon);
6618 Vector3 Z = normalize(cross(X, p2 - p0), epsilon);
6619 Vector3 Y = cross(Z, X);
6620 Vector3 &O = p0;
6621 *z0 = Vector2(0, 0);
6622 *z1 = Vector2(length(p1 - O), 0);
6623 *z2 = Vector2(dot(p2 - O, X), dot(p2 - O, Y));
6624 }
6625
computeLeastSquaresConformalMap(Mesh * mesh)6626 static bool computeLeastSquaresConformalMap(Mesh *mesh)
6627 {
6628 uint32_t lockedVertex0, lockedVertex1;
6629 if (!findApproximateDiameterVertices(mesh, &lockedVertex0, &lockedVertex1)) {
6630 // Mesh has no boundaries.
6631 return false;
6632 }
6633 const uint32_t vertexCount = mesh->vertexCount();
6634 opennl::NLContext *context = opennl::nlNewContext();
6635 opennl::nlSolverParameteri(context, NL_NB_VARIABLES, int(2 * vertexCount));
6636 opennl::nlSolverParameteri(context, NL_MAX_ITERATIONS, int(5 * vertexCount));
6637 opennl::nlBegin(context, NL_SYSTEM);
6638 const Vector2 *texcoords = mesh->texcoords();
6639 for (uint32_t i = 0; i < vertexCount; i++) {
6640 opennl::nlSetVariable(context, 2 * i, texcoords[i].x);
6641 opennl::nlSetVariable(context, 2 * i + 1, texcoords[i].y);
6642 if (i == lockedVertex0 || i == lockedVertex1) {
6643 opennl::nlLockVariable(context, 2 * i);
6644 opennl::nlLockVariable(context, 2 * i + 1);
6645 }
6646 }
6647 opennl::nlBegin(context, NL_MATRIX);
6648 const uint32_t faceCount = mesh->faceCount();
6649 const Vector3 *positions = mesh->positions();
6650 const uint32_t *indices = mesh->indices();
6651 for (uint32_t f = 0; f < faceCount; f++) {
6652 const uint32_t v0 = indices[f * 3 + 0];
6653 const uint32_t v1 = indices[f * 3 + 1];
6654 const uint32_t v2 = indices[f * 3 + 2];
6655 Vector2 z0, z1, z2;
6656 projectTriangle(positions[v0], positions[v1], positions[v2], &z0, &z1, &z2, mesh->epsilon());
6657 double a = z1.x - z0.x;
6658 double b = z1.y - z0.y;
6659 double c = z2.x - z0.x;
6660 double d = z2.y - z0.y;
6661 XA_DEBUG_ASSERT(b == 0.0);
6662 // Note : 2*id + 0 --> u
6663 // 2*id + 1 --> v
6664 uint32_t u0_id = 2 * v0;
6665 uint32_t v0_id = 2 * v0 + 1;
6666 uint32_t u1_id = 2 * v1;
6667 uint32_t v1_id = 2 * v1 + 1;
6668 uint32_t u2_id = 2 * v2;
6669 uint32_t v2_id = 2 * v2 + 1;
6670 // Note : b = 0
6671 // Real part
6672 opennl::nlBegin(context, NL_ROW);
6673 opennl::nlCoefficient(context, u0_id, -a+c) ;
6674 opennl::nlCoefficient(context, v0_id, b-d) ;
6675 opennl::nlCoefficient(context, u1_id, -c) ;
6676 opennl::nlCoefficient(context, v1_id, d) ;
6677 opennl::nlCoefficient(context, u2_id, a);
6678 opennl::nlEnd(context, NL_ROW);
6679 // Imaginary part
6680 opennl::nlBegin(context, NL_ROW);
6681 opennl::nlCoefficient(context, u0_id, -b+d);
6682 opennl::nlCoefficient(context, v0_id, -a+c);
6683 opennl::nlCoefficient(context, u1_id, -d);
6684 opennl::nlCoefficient(context, v1_id, -c);
6685 opennl::nlCoefficient(context, v2_id, a);
6686 opennl::nlEnd(context, NL_ROW);
6687 }
6688 opennl::nlEnd(context, NL_MATRIX);
6689 opennl::nlEnd(context, NL_SYSTEM);
6690 if (!opennl::nlSolve(context)) {
6691 opennl::nlDeleteContext(context);
6692 return false;
6693 }
6694 for (uint32_t i = 0; i < vertexCount; i++) {
6695 const double u = opennl::nlGetVariable(context, 2 * i);
6696 const double v = opennl::nlGetVariable(context, 2 * i + 1);
6697 mesh->texcoord(i) = Vector2((float)u, (float)v);
6698 XA_DEBUG_ASSERT(!isNan(mesh->texcoord(i).x));
6699 XA_DEBUG_ASSERT(!isNan(mesh->texcoord(i).y));
6700 }
6701 opennl::nlDeleteContext(context);
6702 return true;
6703 }
6704
6705 #if XA_RECOMPUTE_CHARTS
6706 struct PiecewiseParam
6707 {
resetxatlas::internal::param::PiecewiseParam6708 void reset(const Mesh *mesh, uint32_t faceCount)
6709 {
6710 m_mesh = mesh;
6711 m_faceCount = faceCount;
6712 const uint32_t vertexCount = m_mesh->vertexCount();
6713 m_texcoords.resize(vertexCount);
6714 m_patch.reserve(m_faceCount);
6715 m_candidates.reserve(m_faceCount);
6716 m_faceInAnyPatch.resize(m_faceCount);
6717 m_faceInAnyPatch.zeroOutMemory();
6718 m_faceInvalid.resize(m_faceCount);
6719 m_faceInPatch.resize(m_faceCount);
6720 m_vertexInPatch.resize(vertexCount);
6721 m_faceToCandidate.resize(m_faceCount);
6722 }
6723
chartFacesxatlas::internal::param::PiecewiseParam6724 ConstArrayView<uint32_t> chartFaces() const { return m_patch; }
texcoordsxatlas::internal::param::PiecewiseParam6725 const Vector2 *texcoords() const { return m_texcoords.data(); }
6726
computeChartxatlas::internal::param::PiecewiseParam6727 bool computeChart()
6728 {
6729 // Clear per-patch state.
6730 m_patch.clear();
6731 m_candidates.clear();
6732 m_faceToCandidate.zeroOutMemory();
6733 m_faceInvalid.zeroOutMemory();
6734 m_faceInPatch.zeroOutMemory();
6735 m_vertexInPatch.zeroOutMemory();
6736 // Add the seed face (first unassigned face) to the patch.
6737 uint32_t seed = UINT32_MAX;
6738 for (uint32_t f = 0; f < m_faceCount; f++) {
6739 if (m_faceInAnyPatch.get(f))
6740 continue;
6741 seed = f;
6742 // Add all 3 vertices.
6743 Vector2 texcoords[3];
6744 orthoProjectFace(seed, texcoords);
6745 for (uint32_t i = 0; i < 3; i++) {
6746 const uint32_t vertex = m_mesh->vertexAt(seed * 3 + i);
6747 m_vertexInPatch.set(vertex);
6748 m_texcoords[vertex] = texcoords[i];
6749 }
6750 addFaceToPatch(seed);
6751 // Initialize the boundary grid.
6752 m_boundaryGrid.reset(m_texcoords.data(), m_mesh->indices());
6753 for (Mesh::FaceEdgeIterator it(m_mesh, seed); !it.isDone(); it.advance())
6754 m_boundaryGrid.append(it.edge());
6755 break;
6756 }
6757 if (seed == UINT32_MAX)
6758 return false;
6759 for (;;) {
6760 // Find the candidate with the lowest cost.
6761 float lowestCost = FLT_MAX;
6762 Candidate *bestCandidate = nullptr;
6763 for (uint32_t i = 0; i < m_candidates.size(); i++) {
6764 Candidate *candidate = m_candidates[i];
6765 if (candidate->maxCost < lowestCost) {
6766 lowestCost = candidate->maxCost;
6767 bestCandidate = candidate;
6768 }
6769 }
6770 if (!bestCandidate)
6771 break;
6772 XA_DEBUG_ASSERT(!bestCandidate->prev); // Must be head of linked candidates.
6773 // Compute the position by averaging linked candidates (candidates that share the same free vertex).
6774 Vector2 position(0.0f);
6775 uint32_t n = 0;
6776 for (CandidateIterator it(bestCandidate); !it.isDone(); it.advance()) {
6777 position += it.current()->position;
6778 n++;
6779 }
6780 position *= 1.0f / (float)n;
6781 const uint32_t freeVertex = bestCandidate->vertex;
6782 XA_DEBUG_ASSERT(!isNan(position.x));
6783 XA_DEBUG_ASSERT(!isNan(position.y));
6784 m_texcoords[freeVertex] = position;
6785 // Check for flipped faces. This is also done when candidates are first added, but the averaged position of the free vertex is different now, so check again.
6786 bool invalid = false;
6787 for (CandidateIterator it(bestCandidate); !it.isDone(); it.advance()) {
6788 const uint32_t vertex0 = m_mesh->vertexAt(meshEdgeIndex0(it.current()->patchEdge));
6789 const uint32_t vertex1 = m_mesh->vertexAt(meshEdgeIndex1(it.current()->patchEdge));
6790 const float freeVertexOrient = orientToEdge(m_texcoords[vertex0], m_texcoords[vertex1], position);
6791 if ((it.current()->patchVertexOrient < 0.0f && freeVertexOrient < 0.0f) || (it.current()->patchVertexOrient > 0.0f && freeVertexOrient > 0.0f)) {
6792 invalid = true;
6793 break;
6794 }
6795 }
6796 // Check for boundary intersection.
6797 if (!invalid) {
6798 XA_PROFILE_START(parameterizeChartsPiecewiseBoundaryIntersection)
6799 // Test candidate edges that would form part of the new patch boundary.
6800 // Ignore boundary edges that would become internal if the candidate faces were added to the patch.
6801 Array<uint32_t> newBoundaryEdges, ignoreEdges;
6802 for (CandidateIterator candidateIt(bestCandidate); !candidateIt.isDone(); candidateIt.advance()) {
6803 for (Mesh::FaceEdgeIterator it(m_mesh, candidateIt.current()->face); !it.isDone(); it.advance()) {
6804 const uint32_t oface = it.oppositeFace();
6805 if (oface == UINT32_MAX || oface >= m_faceCount || !m_faceInPatch.get(oface))
6806 newBoundaryEdges.push_back(it.edge());
6807 if (oface != UINT32_MAX && oface < m_faceCount && m_faceInPatch.get(oface))
6808 ignoreEdges.push_back(it.oppositeEdge());
6809 }
6810 }
6811 invalid = m_boundaryGrid.intersect(m_mesh->epsilon(), newBoundaryEdges, ignoreEdges);
6812 XA_PROFILE_END(parameterizeChartsPiecewiseBoundaryIntersection)
6813 }
6814 if (invalid) {
6815 // Mark all faces of linked candidates as invalid.
6816 for (CandidateIterator it(bestCandidate); !it.isDone(); it.advance())
6817 m_faceInvalid.set(it.current()->face);
6818 removeLinkedCandidates(bestCandidate);
6819 } else {
6820 // Add vertex to the patch.
6821 m_vertexInPatch.set(freeVertex);
6822 // Add faces to the patch.
6823 for (CandidateIterator it(bestCandidate); !it.isDone(); it.advance())
6824 addFaceToPatch(it.current()->face);
6825 // Successfully added candidate face(s) to patch.
6826 removeLinkedCandidates(bestCandidate);
6827 // Reset the grid with all edges on the patch boundary.
6828 XA_PROFILE_START(parameterizeChartsPiecewiseBoundaryIntersection)
6829 m_boundaryGrid.reset(m_texcoords.data(), m_mesh->indices());
6830 for (uint32_t i = 0; i < m_patch.size(); i++) {
6831 for (Mesh::FaceEdgeIterator it(m_mesh, m_patch[i]); !it.isDone(); it.advance()) {
6832 const uint32_t oface = it.oppositeFace();
6833 if (oface == UINT32_MAX || oface >= m_faceCount || !m_faceInPatch.get(oface))
6834 m_boundaryGrid.append(it.edge());
6835 }
6836 }
6837 XA_PROFILE_END(parameterizeChartsPiecewiseBoundaryIntersection)
6838 }
6839 }
6840 return true;
6841 }
6842
6843 private:
6844 struct Candidate
6845 {
6846 uint32_t face, vertex;
6847 Candidate *prev, *next; // The previous/next candidate with the same vertex.
6848 Vector2 position;
6849 float cost;
6850 float maxCost; // Of all linked candidates.
6851 uint32_t patchEdge;
6852 float patchVertexOrient;
6853 };
6854
6855 struct CandidateIterator
6856 {
CandidateIteratorxatlas::internal::param::PiecewiseParam::CandidateIterator6857 CandidateIterator(Candidate *head) : m_current(head) { XA_DEBUG_ASSERT(!head->prev); }
advancexatlas::internal::param::PiecewiseParam::CandidateIterator6858 void advance() { if (m_current != nullptr) { m_current = m_current->next; } }
isDonexatlas::internal::param::PiecewiseParam::CandidateIterator6859 bool isDone() const { return !m_current; }
currentxatlas::internal::param::PiecewiseParam::CandidateIterator6860 Candidate *current() { return m_current; }
6861
6862 private:
6863 Candidate *m_current;
6864 };
6865
6866 const Mesh *m_mesh;
6867 uint32_t m_faceCount;
6868 Array<Vector2> m_texcoords;
6869 BitArray m_faceInAnyPatch; // Face is in a previous chart patch or the current patch.
6870 Array<Candidate *> m_candidates; // Incident faces to the patch.
6871 Array<Candidate *> m_faceToCandidate;
6872 Array<uint32_t> m_patch; // The current chart patch.
6873 BitArray m_faceInPatch, m_vertexInPatch; // Face/vertex is in the current patch.
6874 BitArray m_faceInvalid; // Face cannot be added to the patch - flipped, cost too high or causes boundary intersection.
6875 UniformGrid2 m_boundaryGrid;
6876
addFaceToPatchxatlas::internal::param::PiecewiseParam6877 void addFaceToPatch(uint32_t face)
6878 {
6879 XA_DEBUG_ASSERT(!m_faceInPatch.get(face));
6880 XA_DEBUG_ASSERT(!m_faceInAnyPatch.get(face));
6881 m_patch.push_back(face);
6882 m_faceInPatch.set(face);
6883 m_faceInAnyPatch.set(face);
6884 // Find new candidate faces on the patch incident to the newly added face.
6885 for (Mesh::FaceEdgeIterator it(m_mesh, face); !it.isDone(); it.advance()) {
6886 const uint32_t oface = it.oppositeFace();
6887 if (oface == UINT32_MAX || oface >= m_faceCount || m_faceInAnyPatch.get(oface) || m_faceToCandidate[oface])
6888 continue;
6889 // Found an active edge on the patch front.
6890 // Find the free vertex (the vertex that isn't on the active edge).
6891 // Compute the orientation of the other patch face vertex to the active edge.
6892 uint32_t freeVertex = UINT32_MAX;
6893 float orient = 0.0f;
6894 for (uint32_t j = 0; j < 3; j++) {
6895 const uint32_t vertex = m_mesh->vertexAt(oface * 3 + j);
6896 if (vertex != it.vertex0() && vertex != it.vertex1()) {
6897 freeVertex = vertex;
6898 orient = orientToEdge(m_texcoords[it.vertex0()], m_texcoords[it.vertex1()], m_texcoords[m_mesh->vertexAt(face * 3 + j)]);
6899 break;
6900 }
6901 }
6902 XA_DEBUG_ASSERT(freeVertex != UINT32_MAX);
6903 // If the free vertex is already in the patch, the face is enclosed by the patch. Add the face to the patch - don't need to assign texcoords.
6904 /*if (m_vertexInPatch.get(freeVertex)) {
6905 freeVertex = UINT32_MAX;
6906 addFaceToPatch(oface, false);
6907 continue;
6908 }*/
6909 // Check this here rather than above so faces enclosed by the patch are always added.
6910 if (m_faceInvalid.get(oface))
6911 continue;
6912 addCandidateFace(it.edge(), orient, oface, it.oppositeEdge(), freeVertex);
6913 }
6914 }
6915
addCandidateFacexatlas::internal::param::PiecewiseParam6916 void addCandidateFace(uint32_t patchEdge, float patchVertexOrient, uint32_t face, uint32_t edge, uint32_t freeVertex)
6917 {
6918 XA_DEBUG_ASSERT(!m_faceToCandidate[face]);
6919 Vector2 texcoords[3];
6920 orthoProjectFace(face, texcoords);
6921 // Find corresponding vertices between the patch edge and candidate edge.
6922 const uint32_t vertex0 = m_mesh->vertexAt(meshEdgeIndex0(patchEdge));
6923 const uint32_t vertex1 = m_mesh->vertexAt(meshEdgeIndex1(patchEdge));
6924 uint32_t localVertex0 = UINT32_MAX, localVertex1 = UINT32_MAX, localFreeVertex = UINT32_MAX;
6925 for (uint32_t i = 0; i < 3; i++) {
6926 const uint32_t vertex = m_mesh->vertexAt(face * 3 + i);
6927 if (vertex == m_mesh->vertexAt(meshEdgeIndex1(edge)))
6928 localVertex0 = i;
6929 else if (vertex == m_mesh->vertexAt(meshEdgeIndex0(edge)))
6930 localVertex1 = i;
6931 else
6932 localFreeVertex = i;
6933 }
6934 // Scale orthogonal projection to match the patch edge.
6935 const Vector2 patchEdgeVec = m_texcoords[vertex1] - m_texcoords[vertex0];
6936 const Vector2 localEdgeVec = texcoords[localVertex1] - texcoords[localVertex0];
6937 const float len1 = length(patchEdgeVec);
6938 const float len2 = length(localEdgeVec);
6939 if (len1 <= 0.0f || len2 <= 0.0f)
6940 return; // Zero length edge.
6941 const float scale = len1 / len2;
6942 for (uint32_t i = 0; i < 3; i++)
6943 texcoords[i] *= scale;
6944 // Translate to the first vertex on the patch edge.
6945 const Vector2 translate = m_texcoords[vertex0] - texcoords[localVertex0];
6946 for (uint32_t i = 0; i < 3; i++)
6947 texcoords[i] += translate;
6948 // Compute the angle between the patch edge and the corresponding local edge.
6949 const float angle = atan2f(patchEdgeVec.y, patchEdgeVec.x) - atan2f(localEdgeVec.y, localEdgeVec.x);
6950 // Rotate so the patch edge and the corresponding local edge occupy the same space.
6951 for (uint32_t i = 0; i < 3; i++) {
6952 if (i == localVertex0)
6953 continue;
6954 Vector2 &uv = texcoords[i];
6955 uv -= texcoords[localVertex0]; // Rotate around the first vertex.
6956 const float c = cosf(angle);
6957 const float s = sinf(angle);
6958 const float x = uv.x * c - uv.y * s;
6959 const float y = uv.y * c + uv.x * s;
6960 uv.x = x + texcoords[localVertex0].x;
6961 uv.y = y + texcoords[localVertex0].y;
6962 }
6963 if (isNan(texcoords[localFreeVertex].x) || isNan(texcoords[localFreeVertex].y))
6964 return;
6965 // Check for local overlap (flipped triangle).
6966 // The patch face vertex that isn't on the active edge and the free vertex should be oriented on opposite sides to the active edge.
6967 const float freeVertexOrient = orientToEdge(m_texcoords[vertex0], m_texcoords[vertex1], texcoords[localFreeVertex]);
6968 if ((patchVertexOrient < 0.0f && freeVertexOrient < 0.0f) || (patchVertexOrient > 0.0f && freeVertexOrient > 0.0f)) {
6969 m_faceInvalid.set(face);
6970 return;
6971 }
6972 const float stretch = computeStretch(m_mesh->position(vertex0), m_mesh->position(vertex1), m_mesh->position(freeVertex), texcoords[0], texcoords[1], texcoords[2]);
6973 if (stretch >= FLT_MAX) {
6974 m_faceInvalid.set(face);
6975 return;
6976 }
6977 const float cost = fabsf(stretch - 1.0f);
6978 #if 0
6979 if (cost > 0.25f) {
6980 m_faceInvalid.set(face);
6981 return;
6982 }
6983 #endif
6984 // Add the candidate.
6985 Candidate *candidate = XA_ALLOC(MemTag::Default, Candidate);
6986 candidate->face = face;
6987 candidate->vertex = freeVertex;
6988 candidate->position = texcoords[localFreeVertex];
6989 candidate->prev = candidate->next = nullptr;
6990 candidate->cost = candidate->maxCost = cost;
6991 candidate->patchEdge = patchEdge;
6992 candidate->patchVertexOrient = patchVertexOrient;
6993 m_candidates.push_back(candidate);
6994 m_faceToCandidate[face] = candidate;
6995 // Link with candidates that share the same vertex. Append to tail.
6996 for (uint32_t i = 0; i < m_candidates.size() - 1; i++) {
6997 if (m_candidates[i]->vertex == candidate->vertex) {
6998 Candidate *tail = m_candidates[i];
6999 for (;;) {
7000 if (tail->next)
7001 tail = tail->next;
7002 else
7003 break;
7004 }
7005 candidate->prev = tail;
7006 candidate->next = nullptr;
7007 tail->next = candidate;
7008 break;
7009 }
7010 }
7011 // Set max cost for linked candidates.
7012 Candidate *head = linkedCandidateHead(candidate);
7013 float maxCost = 0.0f;
7014 for (CandidateIterator it(head); !it.isDone(); it.advance())
7015 maxCost = max(maxCost, it.current()->cost);
7016 for (CandidateIterator it(head); !it.isDone(); it.advance())
7017 it.current()->maxCost = maxCost;
7018 }
7019
linkedCandidateHeadxatlas::internal::param::PiecewiseParam7020 Candidate *linkedCandidateHead(Candidate *candidate)
7021 {
7022 Candidate *current = candidate;
7023 for (;;) {
7024 if (!current->prev)
7025 break;
7026 current = current->prev;
7027 }
7028 return current;
7029 }
7030
removeLinkedCandidatesxatlas::internal::param::PiecewiseParam7031 void removeLinkedCandidates(Candidate *head)
7032 {
7033 XA_DEBUG_ASSERT(!head->prev);
7034 Candidate *current = head;
7035 while (current) {
7036 Candidate *next = current->next;
7037 m_faceToCandidate[current->face] = nullptr;
7038 for (uint32_t i = 0; i < m_candidates.size(); i++) {
7039 if (m_candidates[i] == current) {
7040 m_candidates.removeAt(i);
7041 break;
7042 }
7043 }
7044 XA_FREE(current);
7045 current = next;
7046 }
7047 }
7048
orthoProjectFacexatlas::internal::param::PiecewiseParam7049 void orthoProjectFace(uint32_t face, Vector2 *texcoords) const
7050 {
7051 const Vector3 normal = m_mesh->computeFaceNormal(face);
7052 const Vector3 tangent = normalize(m_mesh->position(m_mesh->vertexAt(face * 3 + 1)) - m_mesh->position(m_mesh->vertexAt(face * 3 + 0)), kEpsilon);
7053 const Vector3 bitangent = cross(normal, tangent);
7054 for (uint32_t i = 0; i < 3; i++) {
7055 const Vector3 &pos = m_mesh->position(m_mesh->vertexAt(face * 3 + i));
7056 texcoords[i] = Vector2(dot(tangent, pos), dot(bitangent, pos));
7057 }
7058 }
7059
parametricAreaxatlas::internal::param::PiecewiseParam7060 float parametricArea(const Vector2 *texcoords) const
7061 {
7062 const Vector2 &v1 = texcoords[0];
7063 const Vector2 &v2 = texcoords[1];
7064 const Vector2 &v3 = texcoords[2];
7065 return ((v2.x - v1.x) * (v3.y - v1.y) - (v3.x - v1.x) * (v2.y - v1.y)) * 0.5f;
7066 }
7067
computeStretchxatlas::internal::param::PiecewiseParam7068 float computeStretch(Vector3 p1, Vector3 p2, Vector3 p3, Vector2 t1, Vector2 t2, Vector2 t3) const
7069 {
7070 float parametricArea = ((t2.y - t1.y) * (t3.x - t1.x) - (t3.y - t1.y) * (t2.x - t1.x)) * 0.5f;
7071 if (isZero(parametricArea, kAreaEpsilon))
7072 return FLT_MAX;
7073 if (parametricArea < 0.0f)
7074 parametricArea = fabsf(parametricArea);
7075 const float geometricArea = length(cross(p2 - p1, p3 - p1)) * 0.5f;
7076 if (parametricArea <= geometricArea)
7077 return parametricArea / geometricArea;
7078 else
7079 return geometricArea / parametricArea;
7080 }
7081
7082 // Return value is positive if the point is one side of the edge, negative if on the other side.
orientToEdgexatlas::internal::param::PiecewiseParam7083 float orientToEdge(Vector2 edgeVertex0, Vector2 edgeVertex1, Vector2 point) const
7084 {
7085 return (edgeVertex0.x - point.x) * (edgeVertex1.y - point.y) - (edgeVertex0.y - point.y) * (edgeVertex1.x - point.x);
7086 }
7087 };
7088 #endif
7089
7090 // Estimate quality of existing parameterization.
7091 struct Quality
7092 {
7093 // computeBoundaryIntersection
7094 bool boundaryIntersection = false;
7095
7096 // computeFlippedFaces
7097 uint32_t totalTriangleCount = 0;
7098 uint32_t flippedTriangleCount = 0;
7099 uint32_t zeroAreaTriangleCount = 0;
7100
7101 // computeMetrics
7102 float totalParametricArea = 0.0f;
7103 float totalGeometricArea = 0.0f;
7104 float stretchMetric = 0.0f;
7105 float maxStretchMetric = 0.0f;
7106 float conformalMetric = 0.0f;
7107 float authalicMetric = 0.0f;
7108
computeBoundaryIntersectionxatlas::internal::param::Quality7109 void computeBoundaryIntersection(const Mesh *mesh, UniformGrid2 &boundaryGrid)
7110 {
7111 const Array<uint32_t> &boundaryEdges = mesh->boundaryEdges();
7112 const uint32_t boundaryEdgeCount = boundaryEdges.size();
7113 boundaryGrid.reset(mesh->texcoords(), mesh->indices(), boundaryEdgeCount);
7114 for (uint32_t i = 0; i < boundaryEdgeCount; i++)
7115 boundaryGrid.append(boundaryEdges[i]);
7116 boundaryIntersection = boundaryGrid.intersect(mesh->epsilon());
7117 #if XA_DEBUG_EXPORT_BOUNDARY_GRID
7118 static int exportIndex = 0;
7119 char filename[256];
7120 XA_SPRINTF(filename, sizeof(filename), "debug_boundary_grid_%03d.tga", exportIndex);
7121 boundaryGrid.debugExport(filename);
7122 exportIndex++;
7123 #endif
7124 }
7125
computeFlippedFacesxatlas::internal::param::Quality7126 void computeFlippedFaces(const Mesh *mesh, uint32_t faceCount, Array<uint32_t> *flippedFaces)
7127 {
7128 totalTriangleCount = flippedTriangleCount = zeroAreaTriangleCount = 0;
7129 if (flippedFaces)
7130 flippedFaces->clear();
7131 for (uint32_t f = 0; f < faceCount; f++) {
7132 Vector2 texcoord[3];
7133 for (int i = 0; i < 3; i++) {
7134 const uint32_t v = mesh->vertexAt(f * 3 + i);
7135 texcoord[i] = mesh->texcoord(v);
7136 }
7137 totalTriangleCount++;
7138 const float t1 = texcoord[0].x;
7139 const float s1 = texcoord[0].y;
7140 const float t2 = texcoord[1].x;
7141 const float s2 = texcoord[1].y;
7142 const float t3 = texcoord[2].x;
7143 const float s3 = texcoord[2].y;
7144 const float parametricArea = ((s2 - s1) * (t3 - t1) - (s3 - s1) * (t2 - t1)) * 0.5f;
7145 if (isZero(parametricArea, kAreaEpsilon)) {
7146 zeroAreaTriangleCount++;
7147 continue;
7148 }
7149 if (parametricArea < 0.0f) {
7150 // Count flipped triangles.
7151 flippedTriangleCount++;
7152 if (flippedFaces)
7153 flippedFaces->push_back(f);
7154 }
7155 }
7156 if (flippedTriangleCount + zeroAreaTriangleCount == totalTriangleCount) {
7157 // If all triangles are flipped, then none are.
7158 if (flippedFaces)
7159 flippedFaces->clear();
7160 flippedTriangleCount = 0;
7161 }
7162 if (flippedTriangleCount > totalTriangleCount / 2)
7163 {
7164 // If more than half the triangles are flipped, reverse the flipped / not flipped classification.
7165 flippedTriangleCount = totalTriangleCount - flippedTriangleCount;
7166 if (flippedFaces) {
7167 Array<uint32_t> temp;
7168 flippedFaces->copyTo(temp);
7169 flippedFaces->clear();
7170 for (uint32_t f = 0; f < faceCount; f++) {
7171 bool match = false;
7172 for (uint32_t ff = 0; ff < temp.size(); ff++) {
7173 if (temp[ff] == f) {
7174 match = true;
7175 break;
7176 }
7177 }
7178 if (!match)
7179 flippedFaces->push_back(f);
7180 }
7181 }
7182 }
7183 }
7184
computeMetricsxatlas::internal::param::Quality7185 void computeMetrics(const Mesh *mesh, uint32_t faceCount)
7186 {
7187 totalGeometricArea = totalParametricArea = 0.0f;
7188 stretchMetric = maxStretchMetric = conformalMetric = authalicMetric = 0.0f;
7189 for (uint32_t f = 0; f < faceCount; f++) {
7190 Vector3 pos[3];
7191 Vector2 texcoord[3];
7192 for (int i = 0; i < 3; i++) {
7193 const uint32_t v = mesh->vertexAt(f * 3 + i);
7194 pos[i] = mesh->position(v);
7195 texcoord[i] = mesh->texcoord(v);
7196 }
7197 // Evaluate texture stretch metric. See:
7198 // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe
7199 // - "Mesh Parameterization: Theory and Practice", Siggraph'07 Course Notes, Hormann, Levy & Sheffer.
7200 const float t1 = texcoord[0].x;
7201 const float s1 = texcoord[0].y;
7202 const float t2 = texcoord[1].x;
7203 const float s2 = texcoord[1].y;
7204 const float t3 = texcoord[2].x;
7205 const float s3 = texcoord[2].y;
7206 float parametricArea = ((s2 - s1) * (t3 - t1) - (s3 - s1) * (t2 - t1)) * 0.5f;
7207 if (isZero(parametricArea, kAreaEpsilon))
7208 continue;
7209 if (parametricArea < 0.0f)
7210 parametricArea = fabsf(parametricArea);
7211 const float geometricArea = length(cross(pos[1] - pos[0], pos[2] - pos[0])) / 2;
7212 const Vector3 Ss = (pos[0] * (t2 - t3) + pos[1] * (t3 - t1) + pos[2] * (t1 - t2)) / (2 * parametricArea);
7213 const Vector3 St = (pos[0] * (s3 - s2) + pos[1] * (s1 - s3) + pos[2] * (s2 - s1)) / (2 * parametricArea);
7214 const float a = dot(Ss, Ss); // E
7215 const float b = dot(Ss, St); // F
7216 const float c = dot(St, St); // G
7217 // Compute eigen-values of the first fundamental form:
7218 const float sigma1 = sqrtf(0.5f * max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue.
7219 const float sigma2 = sqrtf(0.5f * max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue.
7220 XA_ASSERT(sigma2 > sigma1 || equal(sigma1, sigma2, kEpsilon));
7221 // isometric: sigma1 = sigma2 = 1
7222 // conformal: sigma1 / sigma2 = 1
7223 // authalic: sigma1 * sigma2 = 1
7224 const float rmsStretch = sqrtf((a + c) * 0.5f);
7225 const float rmsStretch2 = sqrtf((square(sigma1) + square(sigma2)) * 0.5f);
7226 XA_DEBUG_ASSERT(equal(rmsStretch, rmsStretch2, 0.01f));
7227 XA_UNUSED(rmsStretch2);
7228 stretchMetric += square(rmsStretch) * geometricArea;
7229 maxStretchMetric = max(maxStretchMetric, sigma2);
7230 if (!isZero(sigma1, 0.000001f)) {
7231 // sigma1 is zero when geometricArea is zero.
7232 conformalMetric += (sigma2 / sigma1) * geometricArea;
7233 }
7234 authalicMetric += (sigma1 * sigma2) * geometricArea;
7235 // Accumulate total areas.
7236 totalGeometricArea += geometricArea;
7237 totalParametricArea += parametricArea;
7238 }
7239 XA_DEBUG_ASSERT(isFinite(totalParametricArea) && totalParametricArea >= 0);
7240 XA_DEBUG_ASSERT(isFinite(totalGeometricArea) && totalGeometricArea >= 0);
7241 XA_DEBUG_ASSERT(isFinite(stretchMetric));
7242 XA_DEBUG_ASSERT(isFinite(maxStretchMetric));
7243 XA_DEBUG_ASSERT(isFinite(conformalMetric));
7244 XA_DEBUG_ASSERT(isFinite(authalicMetric));
7245 if (totalGeometricArea > 0.0f) {
7246 const float normFactor = sqrtf(totalParametricArea / totalGeometricArea);
7247 stretchMetric = sqrtf(stretchMetric / totalGeometricArea) * normFactor;
7248 maxStretchMetric *= normFactor;
7249 conformalMetric = sqrtf(conformalMetric / totalGeometricArea);
7250 authalicMetric = sqrtf(authalicMetric / totalGeometricArea);
7251 }
7252 }
7253 };
7254
7255 struct ChartWarningFlags
7256 {
7257 enum Enum
7258 {
7259 CloseHolesFailed = 1<<1,
7260 FixTJunctionsDuplicatedEdge = 1<<2,
7261 FixTJunctionsFailed = 1<<3,
7262 TriangulateDuplicatedEdge = 1<<4,
7263 };
7264 };
7265
7266 struct ChartCtorBuffers
7267 {
7268 Array<uint32_t> chartMeshIndices;
7269 Array<uint32_t> unifiedMeshIndices;
7270 Array<uint32_t> boundaryLoops;
7271 };
7272
7273 class Chart
7274 {
7275 public:
Chart(ChartCtorBuffers & buffers,const ParameterizeOptions & options,const Basis & basis,ConstArrayView<uint32_t> faces,const Mesh * sourceMesh,uint32_t chartGroupId,uint32_t chartId)7276 Chart(ChartCtorBuffers &buffers, const ParameterizeOptions &options, const Basis &basis, ConstArrayView<uint32_t> faces, const Mesh *sourceMesh, uint32_t chartGroupId, uint32_t chartId) : m_basis(basis), m_mesh(nullptr), m_unifiedMesh(nullptr), m_unmodifiedUnifiedMesh(nullptr), m_type(ChartType::LSCM), m_warningFlags(0), m_closedHolesCount(0), m_fixedTJunctionsCount(0), m_isInvalid(false)
7277 {
7278 XA_UNUSED(chartGroupId);
7279 XA_UNUSED(chartId);
7280 m_faceToSourceFaceMap.copyFrom(faces.data, faces.length);
7281 const uint32_t approxVertexCount = min(faces.length * 3, sourceMesh->vertexCount());
7282 m_mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, sourceMesh->epsilon(), approxVertexCount, faces.length);
7283 m_unifiedMesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, sourceMesh->epsilon(), approxVertexCount, faces.length);
7284 HashMap<uint32_t, PassthroughHash<uint32_t>> sourceVertexToUnifiedVertexMap(MemTag::Mesh, approxVertexCount), sourceVertexToChartVertexMap(MemTag::Mesh, approxVertexCount);
7285 // Add vertices.
7286 const uint32_t faceCount = m_initialFaceCount = faces.length;
7287 for (uint32_t f = 0; f < faceCount; f++) {
7288 for (uint32_t i = 0; i < 3; i++) {
7289 const uint32_t sourceVertex = sourceMesh->vertexAt(m_faceToSourceFaceMap[f] * 3 + i);
7290 const uint32_t sourceUnifiedVertex = sourceMesh->firstColocal(sourceVertex);
7291 uint32_t unifiedVertex = sourceVertexToUnifiedVertexMap.get(sourceUnifiedVertex);
7292 if (unifiedVertex == UINT32_MAX) {
7293 unifiedVertex = sourceVertexToUnifiedVertexMap.add(sourceUnifiedVertex);
7294 m_unifiedMesh->addVertex(sourceMesh->position(sourceVertex));
7295 }
7296 if (sourceVertexToChartVertexMap.get(sourceVertex) == UINT32_MAX) {
7297 sourceVertexToChartVertexMap.add(sourceVertex);
7298 m_vertexToSourceVertexMap.push_back(sourceVertex);
7299 m_chartVertexToUnifiedVertexMap.push_back(unifiedVertex);
7300 m_mesh->addVertex(sourceMesh->position(sourceVertex), Vector3(0.0f), sourceMesh->texcoord(sourceVertex));
7301 }
7302 }
7303 }
7304 // Add faces.
7305 for (uint32_t f = 0; f < faceCount; f++) {
7306 uint32_t indices[3], unifiedIndices[3];
7307 for (uint32_t i = 0; i < 3; i++) {
7308 const uint32_t sourceVertex = sourceMesh->vertexAt(m_faceToSourceFaceMap[f] * 3 + i);
7309 const uint32_t sourceUnifiedVertex = sourceMesh->firstColocal(sourceVertex);
7310 indices[i] = sourceVertexToChartVertexMap.get(sourceVertex);
7311 XA_DEBUG_ASSERT(indices[i] != UINT32_MAX);
7312 unifiedIndices[i] = sourceVertexToUnifiedVertexMap.get(sourceUnifiedVertex);
7313 XA_DEBUG_ASSERT(unifiedIndices[i] != UINT32_MAX);
7314 }
7315 Mesh::AddFaceResult::Enum result = m_mesh->addFace(indices);
7316 XA_UNUSED(result);
7317 XA_DEBUG_ASSERT(result == Mesh::AddFaceResult::OK);
7318 #if XA_DEBUG
7319 // Unifying colocals may create degenerate edges. e.g. if two triangle vertices are colocal.
7320 for (int i = 0; i < 3; i++) {
7321 const uint32_t index1 = unifiedIndices[i];
7322 const uint32_t index2 = unifiedIndices[(i + 1) % 3];
7323 XA_DEBUG_ASSERT(index1 != index2);
7324 }
7325 #endif
7326 result = m_unifiedMesh->addFace(unifiedIndices);
7327 XA_UNUSED(result);
7328 XA_DEBUG_ASSERT(result == Mesh::AddFaceResult::OK);
7329 }
7330 m_mesh->createBoundaries(); // For AtlasPacker::computeBoundingBox
7331 m_mesh->destroyEdgeMap(); // Only needed it for createBoundaries.
7332 m_unifiedMesh->createBoundaries();
7333 if (meshIsPlanar(*m_unifiedMesh)) {
7334 m_type = ChartType::Planar;
7335 return;
7336 }
7337 m_unifiedMesh->linkBoundaries();
7338 #if XA_DEBUG_EXPORT_OBJ_BEFORE_FIX_TJUNCTION
7339 m_unifiedMesh->writeObjFile("debug_before_fix_tjunction.obj");
7340 #endif
7341 bool duplicatedEdge = false, failed = false;
7342 if (options.fixTJunctions) {
7343 XA_PROFILE_START(fixChartMeshTJunctions)
7344 Mesh *fixedUnifiedMesh = meshFixTJunctions(*m_unifiedMesh, &duplicatedEdge, &failed, &m_fixedTJunctionsCount);
7345 XA_PROFILE_END(fixChartMeshTJunctions)
7346 if (fixedUnifiedMesh) {
7347 if (duplicatedEdge)
7348 m_warningFlags |= ChartWarningFlags::FixTJunctionsDuplicatedEdge;
7349 if (failed)
7350 m_warningFlags |= ChartWarningFlags::FixTJunctionsFailed;
7351 m_unmodifiedUnifiedMesh = m_unifiedMesh;
7352 m_unifiedMesh = fixedUnifiedMesh;
7353 m_unifiedMesh->createBoundaries();
7354 m_unifiedMesh->linkBoundaries();
7355 m_initialFaceCount = m_unifiedMesh->faceCount(); // Fixing t-junctions rewrites faces.
7356 }
7357 }
7358 if (options.closeHoles) {
7359 // See if there are any holes that need closing.
7360 Array<uint32_t> &boundaryLoops = buffers.boundaryLoops;
7361 meshGetBoundaryLoops(*m_unifiedMesh, boundaryLoops);
7362 if (boundaryLoops.size() > 1) {
7363 #if XA_DEBUG_EXPORT_OBJ_CLOSE_HOLES_ERROR
7364 const uint32_t faceCountBeforeHolesClosed = m_unifiedMesh->faceCount();
7365 #endif
7366 // Closing the holes is not always the best solution and does not fix all the problems.
7367 // We need to do some analysis of the holes and the genus to:
7368 // - Find cuts that reduce genus.
7369 // - Find cuts to connect holes.
7370 // - Use minimal spanning trees or seamster.
7371 XA_PROFILE_START(closeChartMeshHoles)
7372 uint32_t holeCount = 0;
7373 #if XA_DEBUG_EXPORT_OBJ_CLOSE_HOLES_ERROR
7374 Array<uint32_t> holeFaceCounts;
7375 failed = !meshCloseHoles(m_unifiedMesh, boundaryLoops, m_basis.normal, &holeFaceCounts);
7376 #else
7377 failed = !meshCloseHoles(m_unifiedMesh, boundaryLoops, m_basis.normal, &holeCount, nullptr);
7378 #endif
7379 XA_PROFILE_END(closeChartMeshHoles)
7380 m_unifiedMesh->createBoundaries();
7381 m_unifiedMesh->linkBoundaries();
7382 meshGetBoundaryLoops(*m_unifiedMesh, boundaryLoops);
7383 if (failed || boundaryLoops.size() > 1)
7384 m_warningFlags |= ChartWarningFlags::CloseHolesFailed;
7385 m_closedHolesCount = holeCount;
7386 #if XA_DEBUG_EXPORT_OBJ_CLOSE_HOLES_ERROR
7387 if (m_warningFlags & ChartWarningFlags::CloseHolesFailed) {
7388 char filename[256];
7389 XA_SPRINTF(filename, sizeof(filename), "debug_mesh_%03u_chartgroup_%03u_chart_%03u_close_holes_error.obj", sourceMesh->id(), chartGroupId, chartId);
7390 FILE *file;
7391 XA_FOPEN(file, filename, "w");
7392 if (file) {
7393 m_unifiedMesh->writeObjVertices(file);
7394 fprintf(file, "s off\n");
7395 fprintf(file, "o object\n");
7396 for (uint32_t i = 0; i < faceCountBeforeHolesClosed; i++)
7397 m_unifiedMesh->writeObjFace(file, i);
7398 uint32_t face = faceCountBeforeHolesClosed;
7399 for (uint32_t i = 0; i < holeFaceCounts.size(); i++) {
7400 fprintf(file, "s off\n");
7401 fprintf(file, "o hole%u\n", i);
7402 for (uint32_t j = 0; j < holeFaceCounts[i]; j++) {
7403 m_unifiedMesh->writeObjFace(file, face);
7404 face++;
7405 }
7406 }
7407 m_unifiedMesh->writeObjBoundaryEges(file);
7408 m_unifiedMesh->writeObjLinkedBoundaries(file);
7409 fclose(file);
7410 }
7411 }
7412 #endif
7413 }
7414 }
7415 }
7416
7417 #if XA_RECOMPUTE_CHARTS
Chart(ChartCtorBuffers & buffers,const Chart * parent,const Mesh * parentMesh,ConstArrayView<uint32_t> faces,const Vector2 * texcoords,const Mesh * sourceMesh)7418 Chart(ChartCtorBuffers &buffers, const Chart *parent, const Mesh *parentMesh, ConstArrayView<uint32_t> faces, const Vector2 *texcoords, const Mesh *sourceMesh) : m_mesh(nullptr), m_unifiedMesh(nullptr), m_unmodifiedUnifiedMesh(nullptr), m_type(ChartType::Piecewise), m_warningFlags(0), m_closedHolesCount(0), m_fixedTJunctionsCount(0), m_isInvalid(false)
7419 {
7420 const uint32_t faceCount = m_initialFaceCount = faces.length;
7421 m_faceToSourceFaceMap.resize(faceCount);
7422 for (uint32_t i = 0; i < faceCount; i++)
7423 m_faceToSourceFaceMap[i] = parent->m_faceToSourceFaceMap[faces[i]]; // Map faces to parent chart source mesh.
7424 // Copy face indices.
7425 m_mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, sourceMesh->epsilon(), m_faceToSourceFaceMap.size() * 3, m_faceToSourceFaceMap.size());
7426 Array<uint32_t> &chartMeshIndices = buffers.chartMeshIndices;
7427 chartMeshIndices.resize(sourceMesh->vertexCount());
7428 chartMeshIndices.fillBytes(0xff);
7429 // Add vertices.
7430 for (uint32_t f = 0; f < faceCount; f++) {
7431 for (uint32_t i = 0; i < 3; i++) {
7432 const uint32_t vertex = sourceMesh->vertexAt(m_faceToSourceFaceMap[f] * 3 + i);
7433 const uint32_t parentVertex = parentMesh->vertexAt(faces[f] * 3 + i);
7434 if (chartMeshIndices[vertex] == (uint32_t)~0) {
7435 chartMeshIndices[vertex] = m_mesh->vertexCount();
7436 m_vertexToSourceVertexMap.push_back(vertex);
7437 m_mesh->addVertex(sourceMesh->position(vertex), Vector3(0.0f), texcoords[parentVertex]);
7438 }
7439 }
7440 }
7441 // Add faces.
7442 for (uint32_t f = 0; f < faceCount; f++) {
7443 uint32_t indices[3];
7444 for (uint32_t i = 0; i < 3; i++) {
7445 const uint32_t vertex = sourceMesh->vertexAt(m_faceToSourceFaceMap[f] * 3 + i);
7446 indices[i] = chartMeshIndices[vertex];
7447 }
7448 Mesh::AddFaceResult::Enum result = m_mesh->addFace(indices);
7449 XA_UNUSED(result);
7450 XA_DEBUG_ASSERT(result == Mesh::AddFaceResult::OK);
7451 }
7452 m_mesh->createBoundaries(); // For AtlasPacker::computeBoundingBox
7453 m_mesh->destroyEdgeMap(); // Only needed it for createBoundaries.
7454 // Need to store texcoords for backup/restore so packing can be run multiple times.
7455 backupTexcoords();
7456 }
7457 #endif
7458
~Chart()7459 ~Chart()
7460 {
7461 if (m_mesh) {
7462 m_mesh->~Mesh();
7463 XA_FREE(m_mesh);
7464 }
7465 destroyUnifiedMesh();
7466 }
7467
isInvalid() const7468 bool isInvalid() const { return m_isInvalid; }
type() const7469 ChartType::Enum type() const { return m_type; }
warningFlags() const7470 uint32_t warningFlags() const { return m_warningFlags; }
closedHolesCount() const7471 uint32_t closedHolesCount() const { return m_closedHolesCount; }
fixedTJunctionsCount() const7472 uint32_t fixedTJunctionsCount() const { return m_fixedTJunctionsCount; }
quality() const7473 const Quality &quality() const { return m_quality; }
initialFaceCount() const7474 uint32_t initialFaceCount() const { return m_initialFaceCount; }
7475 #if XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION
paramFlippedFaces() const7476 const Array<uint32_t> ¶mFlippedFaces() const { return m_paramFlippedFaces; }
7477 #endif
mapFaceToSourceFace(uint32_t i) const7478 uint32_t mapFaceToSourceFace(uint32_t i) const { return m_faceToSourceFaceMap[i]; }
mapChartVertexToSourceVertex(uint32_t i) const7479 uint32_t mapChartVertexToSourceVertex(uint32_t i) const { return m_vertexToSourceVertexMap[i]; }
mesh() const7480 const Mesh *mesh() const { return m_mesh; }
mesh()7481 Mesh *mesh() { return m_mesh; }
unifiedMesh() const7482 const Mesh *unifiedMesh() const { return m_unifiedMesh; }
unmodifiedUnifiedMesh() const7483 const Mesh *unmodifiedUnifiedMesh() const { return m_unmodifiedUnifiedMesh; }
7484
parameterize(const ParameterizeOptions & options,UniformGrid2 & boundaryGrid)7485 void parameterize(const ParameterizeOptions &options, UniformGrid2 &boundaryGrid)
7486 {
7487 XA_PROFILE_START(parameterizeChartsOrthogonal)
7488 {
7489 // Project vertices to plane.
7490 const uint32_t vertexCount = m_unifiedMesh->vertexCount();
7491 for (uint32_t i = 0; i < vertexCount; i++)
7492 m_unifiedMesh->texcoord(i) = Vector2(dot(m_basis.tangent, m_unifiedMesh->position(i)), dot(m_basis.bitangent, m_unifiedMesh->position(i)));
7493 }
7494 XA_PROFILE_END(parameterizeChartsOrthogonal)
7495 // Computing charts checks for flipped triangles and boundary intersection. Don't need to do that again here if chart is planar.
7496 if (m_type != ChartType::Planar) {
7497 XA_PROFILE_START(parameterizeChartsEvaluateQuality)
7498 m_quality.computeBoundaryIntersection(m_unifiedMesh, boundaryGrid);
7499 m_quality.computeFlippedFaces(m_unifiedMesh, m_initialFaceCount, nullptr);
7500 m_quality.computeMetrics(m_unifiedMesh, m_initialFaceCount);
7501 XA_PROFILE_END(parameterizeChartsEvaluateQuality)
7502 // Use orthogonal parameterization if quality is acceptable.
7503 if (!m_quality.boundaryIntersection && m_quality.flippedTriangleCount == 0 && m_quality.totalGeometricArea > 0.0f && m_quality.stretchMetric <= 1.1f && m_quality.maxStretchMetric <= 1.25f)
7504 m_type = ChartType::Ortho;
7505 }
7506 if (m_type == ChartType::LSCM) {
7507 XA_PROFILE_START(parameterizeChartsLSCM)
7508 if (options.func) {
7509 options.func(&m_unifiedMesh->position(0).x, &m_unifiedMesh->texcoord(0).x, m_unifiedMesh->vertexCount(), m_unifiedMesh->indices(), m_unifiedMesh->indexCount());
7510 }
7511 else
7512 computeLeastSquaresConformalMap(m_unifiedMesh);
7513 XA_PROFILE_END(parameterizeChartsLSCM)
7514 XA_PROFILE_START(parameterizeChartsEvaluateQuality)
7515 m_quality.computeBoundaryIntersection(m_unifiedMesh, boundaryGrid);
7516 #if XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION
7517 m_quality.computeFlippedFaces(m_unifiedMesh, m_initialFaceCount, &m_paramFlippedFaces);
7518 #else
7519 m_quality.computeFlippedFaces(m_unifiedMesh, m_initialFaceCount, nullptr);
7520 #endif
7521 // Don't need to call computeMetrics here, that's only used in evaluateOrthoQuality to determine if quality is acceptable enough to use ortho projection.
7522 if (m_quality.boundaryIntersection || m_quality.flippedTriangleCount > 0)
7523 m_isInvalid = true;
7524 XA_PROFILE_END(parameterizeChartsEvaluateQuality)
7525 }
7526 #if XA_DEBUG_ALL_CHARTS_INVALID
7527 m_isInvalid = true;
7528 #endif
7529 // Transfer parameterization from unified mesh to chart mesh.
7530 const uint32_t vertexCount = m_mesh->vertexCount();
7531 for (uint32_t v = 0; v < vertexCount; v++)
7532 m_mesh->texcoord(v) = m_unifiedMesh->texcoord(m_chartVertexToUnifiedVertexMap[v]);
7533 // Can destroy unified mesh now.
7534 // But not if the parameterization is invalid, the unified mesh will be needed for PiecewiseParameterization.
7535 if (!m_isInvalid)
7536 destroyUnifiedMesh();
7537 // Need to store texcoords for backup/restore so packing can be run multiple times.
7538 backupTexcoords();
7539 }
7540
computeParametricBounds() const7541 Vector2 computeParametricBounds() const
7542 {
7543 Vector2 minCorner(FLT_MAX, FLT_MAX);
7544 Vector2 maxCorner(-FLT_MAX, -FLT_MAX);
7545 const uint32_t vertexCount = m_mesh->vertexCount();
7546 for (uint32_t v = 0; v < vertexCount; v++) {
7547 minCorner = min(minCorner, m_mesh->texcoord(v));
7548 maxCorner = max(maxCorner, m_mesh->texcoord(v));
7549 }
7550 return (maxCorner - minCorner) * 0.5f;
7551 }
7552
restoreTexcoords()7553 void restoreTexcoords()
7554 {
7555 memcpy(m_mesh->texcoords(), m_backupTexcoords.data(), m_mesh->vertexCount() * sizeof(Vector2));
7556 }
7557
7558 private:
backupTexcoords()7559 void backupTexcoords()
7560 {
7561 m_backupTexcoords.resize(m_mesh->vertexCount());
7562 memcpy(m_backupTexcoords.data(), m_mesh->texcoords(), m_mesh->vertexCount() * sizeof(Vector2));
7563 }
7564
destroyUnifiedMesh()7565 void destroyUnifiedMesh()
7566 {
7567 if (m_unifiedMesh) {
7568 m_unifiedMesh->~Mesh();
7569 XA_FREE(m_unifiedMesh);
7570 m_unifiedMesh = nullptr;
7571 }
7572 if (m_unmodifiedUnifiedMesh) {
7573 m_unmodifiedUnifiedMesh->~Mesh();
7574 XA_FREE(m_unmodifiedUnifiedMesh);
7575 m_unmodifiedUnifiedMesh = nullptr;
7576 }
7577 // Don't need this when unified meshes are destroyed.
7578 m_chartVertexToUnifiedVertexMap.destroy();
7579 }
7580
7581 Basis m_basis;
7582 Mesh *m_mesh;
7583 Mesh *m_unifiedMesh;
7584 Mesh *m_unmodifiedUnifiedMesh; // Unified mesh before fixing t-junctions. Null if no t-junctions were fixed
7585 ChartType::Enum m_type;
7586 uint32_t m_warningFlags;
7587 uint32_t m_initialFaceCount; // Before fixing T-junctions and/or closing holes.
7588 uint32_t m_closedHolesCount, m_fixedTJunctionsCount;
7589
7590 // List of faces of the source mesh that belong to this chart.
7591 Array<uint32_t> m_faceToSourceFaceMap;
7592
7593 // Map vertices of the chart mesh to vertices of the source mesh.
7594 Array<uint32_t> m_vertexToSourceVertexMap;
7595
7596 Array<uint32_t> m_chartVertexToUnifiedVertexMap;
7597
7598 Array<Vector2> m_backupTexcoords;
7599
7600 Quality m_quality;
7601 #if XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION
7602 Array<uint32_t> m_paramFlippedFaces;
7603 #endif
7604 bool m_isInvalid;
7605 };
7606
7607 struct CreateAndParameterizeChartTaskArgs
7608 {
7609 const Basis *basis;
7610 ThreadLocal<UniformGrid2> *boundaryGrid;
7611 Chart *chart; // output
7612 Array<Chart *> charts; // output (if more than one chart)
7613 ThreadLocal<ChartCtorBuffers> *chartBuffers;
7614 const Mesh *mesh;
7615 const ParameterizeOptions *options;
7616 #if XA_RECOMPUTE_CHARTS
7617 ThreadLocal<PiecewiseParam> *pp;
7618 #endif
7619 ConstArrayView<uint32_t> faces;
7620 uint32_t chartGroupId;
7621 uint32_t chartId;
7622 };
7623
runCreateAndParameterizeChartTask(void * userData)7624 static void runCreateAndParameterizeChartTask(void *userData)
7625 {
7626 auto args = (CreateAndParameterizeChartTaskArgs *)userData;
7627 XA_PROFILE_START(createChartMesh)
7628 args->chart = XA_NEW_ARGS(MemTag::Default, Chart, args->chartBuffers->get(), *args->options, *args->basis, args->faces, args->mesh, args->chartGroupId, args->chartId);
7629 XA_PROFILE_END(createChartMesh)
7630 args->chart->parameterize(*args->options, args->boundaryGrid->get());
7631 #if XA_RECOMPUTE_CHARTS
7632 if (!args->chart->isInvalid())
7633 return;
7634 // Recompute charts with invalid parameterizations.
7635 XA_PROFILE_START(parameterizeChartsRecompute)
7636 Chart *invalidChart = args->chart;
7637 // Fixing t-junctions rewrites unified mesh faces, and we need to map faces back to input mesh. So use the unmodified unified mesh.
7638 const Mesh *invalidMesh = invalidChart->unmodifiedUnifiedMesh();
7639 uint32_t faceCount = 0;
7640 if (invalidMesh) {
7641 faceCount = invalidMesh->faceCount();
7642 } else {
7643 invalidMesh = invalidChart->unifiedMesh();
7644 faceCount = invalidChart->initialFaceCount(); // Not invalidMesh->faceCount(). Don't want faces added by hole closing.
7645 }
7646 PiecewiseParam &pp = args->pp->get();
7647 pp.reset(invalidMesh, faceCount);
7648 #if XA_DEBUG_EXPORT_OBJ_RECOMPUTED_CHARTS
7649 char filename[256];
7650 XA_SPRINTF(filename, sizeof(filename), "debug_mesh_%03u_chartgroup_%03u_chart_%03u_recomputed.obj", args->mesh->id(), args->chartGroupId, args->chartId);
7651 FILE *file;
7652 XA_FOPEN(file, filename, "w");
7653 uint32_t subChartIndex = 0;
7654 #endif
7655 for (;;) {
7656 XA_PROFILE_START(parameterizeChartsPiecewise)
7657 const bool facesRemaining = pp.computeChart();
7658 XA_PROFILE_END(parameterizeChartsPiecewise)
7659 if (!facesRemaining)
7660 break;
7661 Chart *chart = XA_NEW_ARGS(MemTag::Default, Chart, args->chartBuffers->get(), invalidChart, invalidMesh, pp.chartFaces(), pp.texcoords(), args->mesh);
7662 args->charts.push_back(chart);
7663 #if XA_DEBUG_EXPORT_OBJ_RECOMPUTED_CHARTS
7664 if (file) {
7665 for (uint32_t j = 0; j < invalidMesh->vertexCount(); j++) {
7666 fprintf(file, "v %g %g %g\n", invalidMesh->position(j).x, invalidMesh->position(j).y, invalidMesh->position(j).z);
7667 fprintf(file, "vt %g %g\n", pp.texcoords()[j].x, pp.texcoords()[j].y);
7668 }
7669 fprintf(file, "o chart%03u\n", subChartIndex);
7670 fprintf(file, "s off\n");
7671 for (uint32_t f = 0; f < pp.chartFaces().length; f++) {
7672 fprintf(file, "f ");
7673 const uint32_t face = pp.chartFaces()[f];
7674 for (uint32_t j = 0; j < 3; j++) {
7675 const uint32_t index = invalidMesh->vertexCount() * subChartIndex + invalidMesh->vertexAt(face * 3 + j) + 1; // 1-indexed
7676 fprintf(file, "%d/%d/%c", index, index, j == 2 ? '\n' : ' ');
7677 }
7678 }
7679 }
7680 subChartIndex++;
7681 #endif
7682 }
7683 #if XA_DEBUG_EXPORT_OBJ_RECOMPUTED_CHARTS
7684 if (file)
7685 fclose(file);
7686 #endif
7687 XA_PROFILE_END(parameterizeChartsRecompute)
7688 #endif // XA_RECOMPUTE_CHARTS
7689 }
7690
7691 // Set of charts corresponding to mesh faces in the same face group.
7692 class ChartGroup
7693 {
7694 public:
ChartGroup(uint32_t id,const Mesh * sourceMesh,const MeshFaceGroups * sourceMeshFaceGroups,MeshFaceGroups::Handle faceGroup)7695 ChartGroup(uint32_t id, const Mesh *sourceMesh, const MeshFaceGroups *sourceMeshFaceGroups, MeshFaceGroups::Handle faceGroup) : m_id(id), m_sourceMesh(sourceMesh), m_sourceMeshFaceGroups(sourceMeshFaceGroups), m_faceGroup(faceGroup), m_faceCount(0), m_paramAddedChartsCount(0), m_paramDeletedChartsCount(0)
7696 {
7697 }
7698
~ChartGroup()7699 ~ChartGroup()
7700 {
7701 for (uint32_t i = 0; i < m_charts.size(); i++) {
7702 m_charts[i]->~Chart();
7703 XA_FREE(m_charts[i]);
7704 }
7705 }
7706
segmentChartCount() const7707 uint32_t segmentChartCount() const { return m_chartBasis.size(); }
chartCount() const7708 uint32_t chartCount() const { return m_charts.size(); }
chartAt(uint32_t i) const7709 Chart *chartAt(uint32_t i) const { return m_charts[i]; }
faceCount() const7710 uint32_t faceCount() const { return m_faceCount; }
paramAddedChartsCount() const7711 uint32_t paramAddedChartsCount() const { return m_paramAddedChartsCount; }
paramDeletedChartsCount() const7712 uint32_t paramDeletedChartsCount() const { return m_paramDeletedChartsCount; }
7713
computeChartFaces(const ChartOptions & options,segment::Atlas & atlas)7714 void computeChartFaces(const ChartOptions &options, segment::Atlas &atlas)
7715 {
7716 // Create mesh from source mesh, using only the faces in this face group.
7717 XA_PROFILE_START(createChartGroupMesh)
7718 Mesh *mesh = createMesh();
7719 XA_PROFILE_END(createChartGroupMesh)
7720 // Segment mesh into charts (arrays of faces).
7721 #if XA_DEBUG_SINGLE_CHART
7722 m_chartBasis.resize(1);
7723 Fit::computeBasis(&mesh->position(0), mesh->vertexCount(), &m_chartBasis[0]);
7724 m_chartFaces.resize(1 + mesh->faceCount());
7725 m_chartFaces[0] = mesh->faceCount();
7726 for (uint32_t i = 0; i < m_chartFaces.size(); i++)
7727 m_chartFaces[i + 1] = i;
7728 #else
7729 XA_PROFILE_START(buildAtlas)
7730 atlas.reset(mesh, options);
7731 atlas.compute();
7732 XA_PROFILE_END(buildAtlas)
7733 #if XA_DEBUG_EXPORT_OBJ_CHARTS
7734 char filename[256];
7735 XA_SPRINTF(filename, sizeof(filename), "debug_mesh_%03u_chartgroup_%03u_charts.obj", m_sourceMesh->id(), m_id);
7736 FILE *file;
7737 XA_FOPEN(file, filename, "w");
7738 if (file) {
7739 mesh->writeObjVertices(file);
7740 for (uint32_t i = 0; i < atlas.chartCount(); i++) {
7741 fprintf(file, "o chart_%04d\n", i);
7742 fprintf(file, "s off\n");
7743 ConstArrayView<uint32_t> faces = atlas.chartFaces(i);
7744 for (uint32_t f = 0; f < faces.length; f++)
7745 mesh->writeObjFace(file, faces[f]);
7746 }
7747 mesh->writeObjBoundaryEges(file);
7748 mesh->writeObjLinkedBoundaries(file);
7749 fclose(file);
7750 }
7751 #endif
7752 // Destroy mesh.
7753 const uint32_t faceCount = mesh->faceCount();
7754 mesh->~Mesh();
7755 XA_FREE(mesh);
7756 XA_PROFILE_START(copyChartFaces)
7757 // Copy basis.
7758 const uint32_t chartCount = atlas.chartCount();
7759 m_chartBasis.resize(chartCount);
7760 for (uint32_t i = 0; i < chartCount; i++)
7761 m_chartBasis[i] = atlas.chartBasis(i);
7762 // Copy faces from segment::Atlas to m_chartFaces array with <chart 0 face count> <face 0> <face n> <chart 1 face count> etc. encoding.
7763 // segment::Atlas faces refer to the chart group mesh. Map them to the input mesh instead.
7764 m_chartFaces.resize(chartCount + faceCount);
7765 uint32_t offset = 0;
7766 for (uint32_t i = 0; i < chartCount; i++) {
7767 ConstArrayView<uint32_t> faces = atlas.chartFaces(i);
7768 m_chartFaces[offset++] = faces.length;
7769 for (uint32_t j = 0; j < faces.length; j++)
7770 m_chartFaces[offset++] = m_faceToSourceFaceMap[faces[j]];
7771 }
7772 XA_PROFILE_END(copyChartFaces)
7773 #endif
7774 }
7775
7776 #if XA_RECOMPUTE_CHARTS
parameterizeCharts(TaskScheduler * taskScheduler,const ParameterizeOptions & options,ThreadLocal<UniformGrid2> * boundaryGrid,ThreadLocal<ChartCtorBuffers> * chartBuffers,ThreadLocal<PiecewiseParam> * piecewiseParam)7777 void parameterizeCharts(TaskScheduler *taskScheduler, const ParameterizeOptions &options, ThreadLocal<UniformGrid2> *boundaryGrid, ThreadLocal<ChartCtorBuffers> *chartBuffers, ThreadLocal<PiecewiseParam> *piecewiseParam)
7778 #else
7779 void parameterizeCharts(TaskScheduler* taskScheduler, const ParameterizeOptions &options, ThreadLocal<UniformGrid2>* boundaryGrid, ThreadLocal<ChartCtorBuffers>* chartBuffers)
7780 #endif
7781 {
7782 // This function may be called multiple times, so destroy existing charts.
7783 for (uint32_t i = 0; i < m_charts.size(); i++) {
7784 m_charts[i]->~Chart();
7785 XA_FREE(m_charts[i]);
7786 }
7787 m_paramAddedChartsCount = 0;
7788 const uint32_t chartCount = m_chartBasis.size();
7789 Array<CreateAndParameterizeChartTaskArgs> taskArgs;
7790 taskArgs.resize(chartCount);
7791 taskArgs.runCtors(); // Has Array member.
7792 TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(chartCount);
7793 uint32_t offset = 0;
7794 for (uint32_t i = 0; i < chartCount; i++) {
7795 CreateAndParameterizeChartTaskArgs &args = taskArgs[i];
7796 args.basis = &m_chartBasis[i];
7797 args.boundaryGrid = boundaryGrid;
7798 args.chart = nullptr;
7799 args.chartGroupId = m_id;
7800 args.chartId = i;
7801 args.chartBuffers = chartBuffers;
7802 const uint32_t faceCount = m_chartFaces[offset++];
7803 args.faces = ConstArrayView<uint32_t>(&m_chartFaces[offset], faceCount);
7804 offset += faceCount;
7805 args.mesh = m_sourceMesh;
7806 args.options = &options;
7807 #if XA_RECOMPUTE_CHARTS
7808 args.pp = piecewiseParam;
7809 #endif
7810 Task task;
7811 task.userData = &args;
7812 task.func = runCreateAndParameterizeChartTask;
7813 taskScheduler->run(taskGroup, task);
7814 }
7815 taskScheduler->wait(&taskGroup);
7816 #if XA_RECOMPUTE_CHARTS
7817 // Count charts. Skip invalid ones and include new ones added by recomputing.
7818 uint32_t newChartCount = 0;
7819 for (uint32_t i = 0; i < chartCount; i++) {
7820 if (taskArgs[i].chart->isInvalid())
7821 newChartCount += taskArgs[i].charts.size();
7822 else
7823 newChartCount++;
7824 }
7825 m_charts.resize(newChartCount);
7826 // Add valid charts first. Destroy invalid ones.
7827 uint32_t current = 0;
7828 for (uint32_t i = 0; i < chartCount; i++) {
7829 Chart *chart = taskArgs[i].chart;
7830 if (chart->isInvalid()) {
7831 chart->~Chart();
7832 XA_FREE(chart);
7833 m_paramDeletedChartsCount++;
7834 continue;
7835 }
7836 m_charts[current++] = chart;
7837 }
7838 // Now add new charts.
7839 for (uint32_t i = 0; i < chartCount; i++) {
7840 CreateAndParameterizeChartTaskArgs &args = taskArgs[i];
7841 for (uint32_t j = 0; j < args.charts.size(); j++) {
7842 m_charts[current++] = args.charts[j];
7843 m_paramAddedChartsCount++;
7844 }
7845 }
7846 #else // XA_RECOMPUTE_CHARTS
7847 m_charts.resize(chartCount);
7848 for (uint32_t i = 0; i < chartCount; i++)
7849 m_charts[i] = taskArgs[i].chart;
7850 #endif // XA_RECOMPUTE_CHARTS
7851 taskArgs.runDtors(); // Has Array member.
7852 }
7853
7854 private:
createMesh()7855 Mesh *createMesh()
7856 {
7857 XA_DEBUG_ASSERT(m_faceGroup != MeshFaceGroups::kInvalid);
7858 // Create new mesh from the source mesh, using faces that belong to this group.
7859 m_faceToSourceFaceMap.reserve(m_sourceMeshFaceGroups->faceCount(m_faceGroup));
7860 for (MeshFaceGroups::Iterator it(m_sourceMeshFaceGroups, m_faceGroup); !it.isDone(); it.advance())
7861 m_faceToSourceFaceMap.push_back(it.face());
7862 // Only initial meshes has ignored faces. The only flag we care about is HasNormals.
7863 const uint32_t faceCount = m_faceCount = m_faceToSourceFaceMap.size();
7864 XA_DEBUG_ASSERT(faceCount > 0);
7865 const uint32_t approxVertexCount = min(faceCount * 3, m_sourceMesh->vertexCount());
7866 Mesh *mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, m_sourceMesh->epsilon(), approxVertexCount, faceCount, m_sourceMesh->flags() & MeshFlags::HasNormals);
7867 HashMap<uint32_t, PassthroughHash<uint32_t>> sourceVertexToVertexMap(MemTag::Mesh, approxVertexCount);
7868 for (uint32_t f = 0; f < faceCount; f++) {
7869 const uint32_t face = m_faceToSourceFaceMap[f];
7870 for (uint32_t i = 0; i < 3; i++) {
7871 const uint32_t vertex = m_sourceMesh->vertexAt(face * 3 + i);
7872 if (sourceVertexToVertexMap.get(vertex) == UINT32_MAX) {
7873 sourceVertexToVertexMap.add(vertex);
7874 Vector3 normal(0.0f);
7875 if (m_sourceMesh->flags() & MeshFlags::HasNormals)
7876 normal = m_sourceMesh->normal(vertex);
7877 mesh->addVertex(m_sourceMesh->position(vertex), normal, m_sourceMesh->texcoord(vertex));
7878 }
7879 }
7880 }
7881 // Add faces.
7882 for (uint32_t f = 0; f < faceCount; f++) {
7883 const uint32_t face = m_faceToSourceFaceMap[f];
7884 XA_DEBUG_ASSERT(!m_sourceMesh->isFaceIgnored(face));
7885 uint32_t indices[3];
7886 for (uint32_t i = 0; i < 3; i++) {
7887 const uint32_t vertex = m_sourceMesh->vertexAt(face * 3 + i);
7888 indices[i] = sourceVertexToVertexMap.get(vertex);
7889 XA_DEBUG_ASSERT(indices[i] != UINT32_MAX);
7890 }
7891 // Don't copy flags - ignored faces aren't used by chart groups, they are handled by InvalidMeshGeometry.
7892 Mesh::AddFaceResult::Enum result = mesh->addFace(indices);
7893 XA_UNUSED(result);
7894 XA_DEBUG_ASSERT(result == Mesh::AddFaceResult::OK);
7895 }
7896 XA_PROFILE_START(createChartGroupMeshColocals)
7897 mesh->createColocals();
7898 XA_PROFILE_END(createChartGroupMeshColocals)
7899 XA_PROFILE_START(createChartGroupMeshBoundaries)
7900 mesh->createBoundaries();
7901 mesh->destroyEdgeMap(); // Only needed it for createBoundaries.
7902 XA_PROFILE_END(createChartGroupMeshBoundaries)
7903 #if XA_DEBUG_EXPORT_OBJ_CHART_GROUPS
7904 char filename[256];
7905 XA_SPRINTF(filename, sizeof(filename), "debug_mesh_%03u_chartgroup_%03u.obj", m_sourceMesh->id(), m_id);
7906 mesh->writeObjFile(filename);
7907 #endif
7908 return mesh;
7909 }
7910
7911 const uint32_t m_id;
7912 const Mesh * const m_sourceMesh;
7913 const MeshFaceGroups * const m_sourceMeshFaceGroups;
7914 const MeshFaceGroups::Handle m_faceGroup;
7915 Array<uint32_t> m_faceToSourceFaceMap; // List of faces of the source mesh that belong to this chart group.
7916 Array<Basis> m_chartBasis; // Copied from segment::Atlas.
7917 Array<uint32_t> m_chartFaces; // Copied from segment::Atlas. Encoding: <chart 0 face count> <face 0> <face n> <chart 1 face count> etc.
7918 Array<Chart *> m_charts;
7919 uint32_t m_faceCount; // Set by createMesh(). Used for sorting.
7920 uint32_t m_paramAddedChartsCount; // Number of new charts added by recomputing charts with invalid parameterizations.
7921 uint32_t m_paramDeletedChartsCount; // Number of charts with invalid parameterizations that were deleted, after charts were recomputed.
7922 };
7923
7924 // References invalid faces and vertices in a mesh.
7925 struct InvalidMeshGeometry
7926 {
7927 // Invalid faces have the face groups MeshFaceGroups::kInvalid.
extractxatlas::internal::param::InvalidMeshGeometry7928 void extract(const Mesh *mesh, const MeshFaceGroups *meshFaceGroups)
7929 {
7930 // Copy invalid faces.
7931 m_faces.clear();
7932 const uint32_t meshFaceCount = mesh->faceCount();
7933 for (uint32_t f = 0; f < meshFaceCount; f++) {
7934 if (meshFaceGroups->groupAt(f) == MeshFaceGroups::kInvalid)
7935 m_faces.push_back(f);
7936 }
7937 // Create *unique* list of vertices of invalid faces.
7938 const uint32_t faceCount = m_faces.size();
7939 m_indices.resize(faceCount * 3);
7940 const uint32_t approxVertexCount = min(faceCount * 3, mesh->vertexCount());
7941 m_vertexToSourceVertexMap.clear();
7942 m_vertexToSourceVertexMap.reserve(approxVertexCount);
7943 HashMap<uint32_t, PassthroughHash<uint32_t>> sourceVertexToVertexMap(MemTag::Mesh, approxVertexCount);
7944 for (uint32_t f = 0; f < faceCount; f++) {
7945 const uint32_t face = m_faces[f];
7946 for (uint32_t i = 0; i < 3; i++) {
7947 const uint32_t vertex = mesh->vertexAt(face * 3 + i);
7948 uint32_t newVertex = sourceVertexToVertexMap.get(vertex);
7949 if (newVertex == UINT32_MAX) {
7950 newVertex = sourceVertexToVertexMap.add(vertex);
7951 m_vertexToSourceVertexMap.push_back(vertex);
7952 }
7953 m_indices[f * 3 + i] = newVertex;
7954 }
7955 }
7956 }
7957
facesxatlas::internal::param::InvalidMeshGeometry7958 ConstArrayView<uint32_t> faces() const { return m_faces; }
indicesxatlas::internal::param::InvalidMeshGeometry7959 ConstArrayView<uint32_t> indices() const { return m_indices; }
verticesxatlas::internal::param::InvalidMeshGeometry7960 ConstArrayView<uint32_t> vertices() const { return m_vertexToSourceVertexMap; }
7961
7962 private:
7963 Array<uint32_t> m_faces, m_indices;
7964 Array<uint32_t> m_vertexToSourceVertexMap; // Map face vertices to vertices of the source mesh.
7965 };
7966
7967 struct ChartGroupComputeChartFacesTaskArgs
7968 {
7969 ThreadLocal<segment::Atlas> *atlas;
7970 ChartGroup *chartGroup;
7971 const ChartOptions *options;
7972 Progress *progress;
7973 };
7974
runChartGroupComputeChartFacesJob(void * userData)7975 static void runChartGroupComputeChartFacesJob(void *userData)
7976 {
7977 auto args = (ChartGroupComputeChartFacesTaskArgs *)userData;
7978 if (args->progress->cancel)
7979 return;
7980 XA_PROFILE_START(chartGroupComputeChartsThread)
7981 args->chartGroup->computeChartFaces(*args->options, args->atlas->get());
7982 XA_PROFILE_END(chartGroupComputeChartsThread)
7983 }
7984
7985 struct MeshComputeChartFacesTaskArgs
7986 {
7987 Array<ChartGroup *> *chartGroups; // output
7988 InvalidMeshGeometry *invalidMeshGeometry; // output
7989 ThreadLocal<segment::Atlas> *atlas;
7990 const ChartOptions *options;
7991 Progress *progress;
7992 const Mesh *sourceMesh;
7993 TaskScheduler *taskScheduler;
7994 };
7995
7996 #if XA_DEBUG_EXPORT_OBJ_FACE_GROUPS
7997 static uint32_t s_faceGroupsCurrentVertex = 0;
7998 #endif
7999
runMeshComputeChartFacesJob(void * userData)8000 static void runMeshComputeChartFacesJob(void *userData)
8001 {
8002 auto args = (MeshComputeChartFacesTaskArgs *)userData;
8003 if (args->progress->cancel)
8004 return;
8005 XA_PROFILE_START(computeChartsThread)
8006 // Create face groups.
8007 XA_PROFILE_START(createFaceGroups)
8008 MeshFaceGroups *meshFaceGroups = XA_NEW_ARGS(MemTag::Mesh, MeshFaceGroups, args->sourceMesh);
8009 meshFaceGroups->compute();
8010 const uint32_t chartGroupCount = meshFaceGroups->groupCount();
8011 XA_PROFILE_END(createFaceGroups)
8012 if (args->progress->cancel)
8013 goto cleanup;
8014 #if XA_DEBUG_EXPORT_OBJ_FACE_GROUPS
8015 {
8016 static std::mutex s_mutex;
8017 std::lock_guard<std::mutex> lock(s_mutex);
8018 char filename[256];
8019 XA_SPRINTF(filename, sizeof(filename), "debug_face_groups.obj");
8020 FILE *file;
8021 XA_FOPEN(file, filename, s_faceGroupsCurrentVertex == 0 ? "w" : "a");
8022 if (file) {
8023 const Mesh *mesh = args->sourceMesh;
8024 mesh->writeObjVertices(file);
8025 // groups
8026 uint32_t numGroups = 0;
8027 for (uint32_t i = 0; i < mesh->faceCount(); i++) {
8028 if (meshFaceGroups->groupAt(i) != MeshFaceGroups::kInvalid)
8029 numGroups = max(numGroups, meshFaceGroups->groupAt(i) + 1);
8030 }
8031 for (uint32_t i = 0; i < numGroups; i++) {
8032 fprintf(file, "o mesh_%03u_group_%04d\n", mesh->id(), i);
8033 fprintf(file, "s off\n");
8034 for (uint32_t f = 0; f < mesh->faceCount(); f++) {
8035 if (meshFaceGroups->groupAt(f) == i)
8036 mesh->writeObjFace(file, f, s_faceGroupsCurrentVertex);
8037 }
8038 }
8039 fprintf(file, "o mesh_%03u_group_ignored\n", mesh->id());
8040 fprintf(file, "s off\n");
8041 for (uint32_t f = 0; f < mesh->faceCount(); f++) {
8042 if (meshFaceGroups->groupAt(f) == MeshFaceGroups::kInvalid)
8043 mesh->writeObjFace(file, f, s_faceGroupsCurrentVertex);
8044 }
8045 mesh->writeObjBoundaryEges(file);
8046 s_faceGroupsCurrentVertex += mesh->vertexCount();
8047 fclose(file);
8048 }
8049 }
8050 #endif
8051 // Create a chart group for each face group.
8052 args->chartGroups->resize(chartGroupCount);
8053 for (uint32_t i = 0; i < chartGroupCount; i++)
8054 (*args->chartGroups)[i] = XA_NEW_ARGS(MemTag::Default, ChartGroup, i, args->sourceMesh, meshFaceGroups, MeshFaceGroups::Handle(i));
8055 // Extract invalid geometry via the invalid face group (MeshFaceGroups::kInvalid).
8056 XA_PROFILE_START(extractInvalidMeshGeometry)
8057 args->invalidMeshGeometry->extract(args->sourceMesh, meshFaceGroups);
8058 XA_PROFILE_END(extractInvalidMeshGeometry)
8059 // One task for each chart group - compute chart faces.
8060 {
8061 XA_PROFILE_START(chartGroupComputeChartsReal)
8062 Array<ChartGroupComputeChartFacesTaskArgs> taskArgs;
8063 taskArgs.resize(chartGroupCount);
8064 for (uint32_t i = 0; i < chartGroupCount; i++) {
8065 taskArgs[i].atlas = args->atlas;
8066 taskArgs[i].chartGroup = (*args->chartGroups)[i];
8067 taskArgs[i].options = args->options;
8068 taskArgs[i].progress = args->progress;
8069 }
8070 TaskGroupHandle taskGroup = args->taskScheduler->createTaskGroup(chartGroupCount);
8071 for (uint32_t i = 0; i < chartGroupCount; i++) {
8072 Task task;
8073 task.userData = &taskArgs[i];
8074 task.func = runChartGroupComputeChartFacesJob;
8075 args->taskScheduler->run(taskGroup, task);
8076 }
8077 args->taskScheduler->wait(&taskGroup);
8078 XA_PROFILE_END(chartGroupComputeChartsReal)
8079 }
8080 XA_PROFILE_END(computeChartsThread)
8081 args->progress->value++;
8082 args->progress->update();
8083 cleanup:
8084 if (meshFaceGroups) {
8085 meshFaceGroups->~MeshFaceGroups();
8086 XA_FREE(meshFaceGroups);
8087 }
8088 }
8089
8090 struct ParameterizeChartsTaskArgs
8091 {
8092 TaskScheduler *taskScheduler;
8093 ChartGroup *chartGroup;
8094 const ParameterizeOptions *options;
8095 ThreadLocal<UniformGrid2> *boundaryGrid;
8096 ThreadLocal<ChartCtorBuffers> *chartBuffers;
8097 #if XA_RECOMPUTE_CHARTS
8098 ThreadLocal<PiecewiseParam> *piecewiseParam;
8099 #endif
8100 Progress *progress;
8101 };
8102
runParameterizeChartsJob(void * userData)8103 static void runParameterizeChartsJob(void *userData)
8104 {
8105 auto args = (ParameterizeChartsTaskArgs *)userData;
8106 if (args->progress->cancel)
8107 return;
8108 XA_PROFILE_START(parameterizeChartsThread)
8109 #if XA_RECOMPUTE_CHARTS
8110 args->chartGroup->parameterizeCharts(args->taskScheduler, *args->options, args->boundaryGrid, args->chartBuffers, args->piecewiseParam);
8111 #else
8112 args->chartGroup->parameterizeCharts(args->taskScheduler, *args->options, args->boundaryGrid, args->chartBuffers);
8113 #endif
8114 XA_PROFILE_END(parameterizeChartsThread)
8115 args->progress->value++;
8116 args->progress->update();
8117 }
8118
8119 /// An atlas is a set of chart groups.
8120 class Atlas
8121 {
8122 public:
Atlas()8123 Atlas() : m_chartsComputed(false), m_chartsParameterized(false) {}
8124
~Atlas()8125 ~Atlas()
8126 {
8127 for (uint32_t i = 0; i < m_meshChartGroups.size(); i++) {
8128 for (uint32_t j = 0; j < m_meshChartGroups[i].size(); j++) {
8129 m_meshChartGroups[i][j]->~ChartGroup();
8130 XA_FREE(m_meshChartGroups[i][j]);
8131 }
8132 }
8133 m_meshChartGroups.runDtors();
8134 m_invalidMeshGeometry.runDtors();
8135 }
8136
meshCount() const8137 uint32_t meshCount() const { return m_meshes.size(); }
invalidMeshGeometry(uint32_t meshIndex) const8138 const InvalidMeshGeometry &invalidMeshGeometry(uint32_t meshIndex) const { return m_invalidMeshGeometry[meshIndex]; }
chartsComputed() const8139 bool chartsComputed() const { return m_chartsComputed; }
chartsParameterized() const8140 bool chartsParameterized() const { return m_chartsParameterized; }
chartGroupCount(uint32_t mesh) const8141 uint32_t chartGroupCount(uint32_t mesh) const { return m_meshChartGroups[mesh].size(); }
chartGroupAt(uint32_t mesh,uint32_t group) const8142 const ChartGroup *chartGroupAt(uint32_t mesh, uint32_t group) const { return m_meshChartGroups[mesh][group]; }
8143
addMesh(const Mesh * mesh)8144 void addMesh(const Mesh *mesh)
8145 {
8146 m_meshes.push_back(mesh);
8147 }
8148
computeCharts(TaskScheduler * taskScheduler,const ChartOptions & options,ProgressFunc progressFunc,void * progressUserData)8149 bool computeCharts(TaskScheduler *taskScheduler, const ChartOptions &options, ProgressFunc progressFunc, void *progressUserData)
8150 {
8151 #if XA_DEBUG_EXPORT_OBJ_PLANAR_REGIONS
8152 segment::s_planarRegionsCurrentRegion = segment::s_planarRegionsCurrentVertex = 0;
8153 #endif
8154 m_chartsComputed = false;
8155 m_chartsParameterized = false;
8156 // Clear chart groups, since this function may be called multiple times.
8157 if (!m_meshChartGroups.isEmpty()) {
8158 for (uint32_t i = 0; i < m_meshChartGroups.size(); i++) {
8159 for (uint32_t j = 0; j < m_meshChartGroups[i].size(); j++) {
8160 m_meshChartGroups[i][j]->~ChartGroup();
8161 XA_FREE(m_meshChartGroups[i][j]);
8162 }
8163 m_meshChartGroups[i].clear();
8164 }
8165 XA_ASSERT(m_meshChartGroups.size() == m_meshes.size()); // The number of meshes shouldn't have changed.
8166 }
8167 m_meshChartGroups.resize(m_meshes.size());
8168 m_meshChartGroups.runCtors();
8169 m_invalidMeshGeometry.resize(m_meshes.size());
8170 m_invalidMeshGeometry.runCtors();
8171 // One task per mesh.
8172 const uint32_t meshCount = m_meshes.size();
8173 Progress progress(ProgressCategory::ComputeCharts, progressFunc, progressUserData, meshCount);
8174 ThreadLocal<segment::Atlas> atlas;
8175 Array<MeshComputeChartFacesTaskArgs> taskArgs;
8176 taskArgs.resize(meshCount);
8177 for (uint32_t i = 0; i < meshCount; i++) {
8178 MeshComputeChartFacesTaskArgs &args = taskArgs[i];
8179 args.atlas = &atlas;
8180 args.chartGroups = &m_meshChartGroups[i];
8181 args.invalidMeshGeometry = &m_invalidMeshGeometry[i];
8182 args.options = &options;
8183 args.progress = &progress;
8184 args.sourceMesh = m_meshes[i];
8185 args.taskScheduler = taskScheduler;
8186 }
8187 // Sort meshes by indexCount.
8188 Array<float> meshSortData;
8189 meshSortData.resize(meshCount);
8190 for (uint32_t i = 0; i < meshCount; i++)
8191 meshSortData[i] = (float)m_meshes[i]->indexCount();
8192 RadixSort meshSort;
8193 meshSort.sort(meshSortData);
8194 // Larger meshes are added first to reduce the chance of thread starvation.
8195 TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(meshCount);
8196 for (uint32_t i = 0; i < meshCount; i++) {
8197 Task task;
8198 task.userData = &taskArgs[meshSort.ranks()[meshCount - i - 1]];
8199 task.func = runMeshComputeChartFacesJob;
8200 taskScheduler->run(taskGroup, task);
8201 }
8202 taskScheduler->wait(&taskGroup);
8203 if (progress.cancel)
8204 return false;
8205 m_chartsComputed = true;
8206 return true;
8207 }
8208
parameterizeCharts(TaskScheduler * taskScheduler,const ParameterizeOptions & options,ProgressFunc progressFunc,void * progressUserData)8209 bool parameterizeCharts(TaskScheduler *taskScheduler, const ParameterizeOptions &options, ProgressFunc progressFunc, void *progressUserData)
8210 {
8211 m_chartsParameterized = false;
8212 uint32_t chartGroupCount = 0;
8213 for (uint32_t i = 0; i < m_meshChartGroups.size(); i++)
8214 chartGroupCount += m_meshChartGroups[i].size();
8215 Progress progress(ProgressCategory::ParameterizeCharts, progressFunc, progressUserData, chartGroupCount);
8216 ThreadLocal<UniformGrid2> boundaryGrid; // For Quality boundary intersection.
8217 ThreadLocal<ChartCtorBuffers> chartBuffers;
8218 #if XA_RECOMPUTE_CHARTS
8219 ThreadLocal<PiecewiseParam> piecewiseParam;
8220 #endif
8221 Array<ParameterizeChartsTaskArgs> taskArgs;
8222 taskArgs.resize(chartGroupCount);
8223 {
8224 uint32_t k = 0;
8225 for (uint32_t i = 0; i < m_meshChartGroups.size(); i++) {
8226 const uint32_t count = m_meshChartGroups[i].size();
8227 for (uint32_t j = 0; j < count; j++) {
8228 ParameterizeChartsTaskArgs &args = taskArgs[k];
8229 args.taskScheduler = taskScheduler;
8230 args.chartGroup = m_meshChartGroups[i][j];
8231 args.options = &options;
8232 args.boundaryGrid = &boundaryGrid;
8233 args.chartBuffers = &chartBuffers;
8234 #if XA_RECOMPUTE_CHARTS
8235 args.piecewiseParam = &piecewiseParam;
8236 #endif
8237 args.progress = &progress;
8238 k++;
8239 }
8240 }
8241 }
8242 // Sort chart groups by face count.
8243 Array<float> chartGroupSortData;
8244 chartGroupSortData.resize(chartGroupCount);
8245 {
8246 uint32_t k = 0;
8247 for (uint32_t i = 0; i < m_meshChartGroups.size(); i++) {
8248 const uint32_t count = m_meshChartGroups[i].size();
8249 for (uint32_t j = 0; j < count; j++) {
8250 chartGroupSortData[k++] = (float)m_meshChartGroups[i][j]->faceCount();
8251 }
8252 }
8253 }
8254 RadixSort chartGroupSort;
8255 chartGroupSort.sort(chartGroupSortData);
8256 // Larger chart groups are added first to reduce the chance of thread starvation.
8257 TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(chartGroupCount);
8258 for (uint32_t i = 0; i < chartGroupCount; i++) {
8259 Task task;
8260 task.userData = &taskArgs[chartGroupSort.ranks()[chartGroupCount - i - 1]];
8261 task.func = runParameterizeChartsJob;
8262 taskScheduler->run(taskGroup, task);
8263 }
8264 taskScheduler->wait(&taskGroup);
8265 if (progress.cancel)
8266 return false;
8267 m_chartsParameterized = true;
8268 return true;
8269 }
8270
8271 private:
8272 Array<const Mesh *> m_meshes;
8273 Array<InvalidMeshGeometry> m_invalidMeshGeometry; // 1 per mesh.
8274 Array<Array<ChartGroup *> > m_meshChartGroups;
8275 bool m_chartsComputed;
8276 bool m_chartsParameterized;
8277 };
8278
8279 } // namespace param
8280
8281 namespace pack {
8282
8283 class AtlasImage
8284 {
8285 public:
AtlasImage(uint32_t width,uint32_t height)8286 AtlasImage(uint32_t width, uint32_t height) : m_width(width), m_height(height)
8287 {
8288 m_data.resize(m_width * m_height);
8289 memset(m_data.data(), 0, sizeof(uint32_t) * m_data.size());
8290 }
8291
resize(uint32_t width,uint32_t height)8292 void resize(uint32_t width, uint32_t height)
8293 {
8294 Array<uint32_t> data;
8295 data.resize(width * height);
8296 memset(data.data(), 0, sizeof(uint32_t) * data.size());
8297 for (uint32_t y = 0; y < min(m_height, height); y++)
8298 memcpy(&data[y * width], &m_data[y * m_width], min(m_width, width) * sizeof(uint32_t));
8299 m_width = width;
8300 m_height = height;
8301 data.moveTo(m_data);
8302 }
8303
addChart(uint32_t chartIndex,const BitImage * image,const BitImage * imageBilinear,const BitImage * imagePadding,int atlas_w,int atlas_h,int offset_x,int offset_y)8304 void addChart(uint32_t chartIndex, const BitImage *image, const BitImage *imageBilinear, const BitImage *imagePadding, int atlas_w, int atlas_h, int offset_x, int offset_y)
8305 {
8306 const int w = image->width();
8307 const int h = image->height();
8308 for (int y = 0; y < h; y++) {
8309 const int yy = y + offset_y;
8310 if (yy < 0)
8311 continue;
8312 for (int x = 0; x < w; x++) {
8313 const int xx = x + offset_x;
8314 if (xx >= 0 && xx < atlas_w && yy < atlas_h) {
8315 const uint32_t dataOffset = xx + yy * m_width;
8316 if (image->get(x, y)) {
8317 XA_DEBUG_ASSERT(m_data[dataOffset] == 0);
8318 m_data[dataOffset] = chartIndex | kImageHasChartIndexBit;
8319 } else if (imageBilinear && imageBilinear->get(x, y)) {
8320 XA_DEBUG_ASSERT(m_data[dataOffset] == 0);
8321 m_data[dataOffset] = chartIndex | kImageHasChartIndexBit | kImageIsBilinearBit;
8322 } else if (imagePadding && imagePadding->get(x, y)) {
8323 XA_DEBUG_ASSERT(m_data[dataOffset] == 0);
8324 m_data[dataOffset] = chartIndex | kImageHasChartIndexBit | kImageIsPaddingBit;
8325 }
8326 }
8327 }
8328 }
8329 }
8330
copyTo(uint32_t * dest,uint32_t destWidth,uint32_t destHeight,int padding) const8331 void copyTo(uint32_t *dest, uint32_t destWidth, uint32_t destHeight, int padding) const
8332 {
8333 for (uint32_t y = 0; y < destHeight; y++)
8334 memcpy(&dest[y * destWidth], &m_data[padding + (y + padding) * m_width], destWidth * sizeof(uint32_t));
8335 }
8336
8337 #if XA_DEBUG_EXPORT_ATLAS_IMAGES
writeTga(const char * filename,uint32_t width,uint32_t height) const8338 void writeTga(const char *filename, uint32_t width, uint32_t height) const
8339 {
8340 Array<uint8_t> image;
8341 image.resize(width * height * 3);
8342 for (uint32_t y = 0; y < height; y++) {
8343 if (y >= m_height)
8344 continue;
8345 for (uint32_t x = 0; x < width; x++) {
8346 if (x >= m_width)
8347 continue;
8348 const uint32_t data = m_data[x + y * m_width];
8349 uint8_t *bgr = &image[(x + y * width) * 3];
8350 if (data == 0) {
8351 bgr[0] = bgr[1] = bgr[2] = 0;
8352 continue;
8353 }
8354 const uint32_t chartIndex = data & kImageChartIndexMask;
8355 if (data & kImageIsPaddingBit) {
8356 bgr[0] = 0;
8357 bgr[1] = 0;
8358 bgr[2] = 255;
8359 } else if (data & kImageIsBilinearBit) {
8360 bgr[0] = 0;
8361 bgr[1] = 255;
8362 bgr[2] = 0;
8363 } else {
8364 const int mix = 192;
8365 srand((unsigned int)chartIndex);
8366 bgr[0] = uint8_t((rand() % 255 + mix) * 0.5f);
8367 bgr[1] = uint8_t((rand() % 255 + mix) * 0.5f);
8368 bgr[2] = uint8_t((rand() % 255 + mix) * 0.5f);
8369 }
8370 }
8371 }
8372 WriteTga(filename, image.data(), width, height);
8373 }
8374 #endif
8375
8376 private:
8377 uint32_t m_width, m_height;
8378 Array<uint32_t> m_data;
8379 };
8380
8381 struct Chart
8382 {
8383 int32_t atlasIndex;
8384 uint32_t material;
8385 uint32_t indexCount;
8386 const uint32_t *indices;
8387 float parametricArea;
8388 float surfaceArea;
8389 Vector2 *vertices;
8390 uint32_t vertexCount;
8391 Array<uint32_t> uniqueVertices;
8392 bool allowRotate;
8393 // bounding box
8394 Vector2 majorAxis, minorAxis, minCorner, maxCorner;
8395 // Mesh only
8396 const Array<uint32_t> *boundaryEdges;
8397 // UvMeshChart only
8398 Array<uint32_t> faces;
8399
uniqueVertexAtxatlas::internal::pack::Chart8400 Vector2 &uniqueVertexAt(uint32_t v) { return uniqueVertices.isEmpty() ? vertices[v] : vertices[uniqueVertices[v]]; }
uniqueVertexCountxatlas::internal::pack::Chart8401 uint32_t uniqueVertexCount() const { return uniqueVertices.isEmpty() ? vertexCount : uniqueVertices.size(); }
8402 };
8403
8404 struct AddChartTaskArgs
8405 {
8406 ThreadLocal<BoundingBox2D> *boundingBox;
8407 param::Chart *paramChart;
8408 Chart *chart; // out
8409 };
8410
runAddChartTask(void * userData)8411 static void runAddChartTask(void *userData)
8412 {
8413 XA_PROFILE_START(packChartsAddChartsThread)
8414 auto args = (AddChartTaskArgs *)userData;
8415 param::Chart *paramChart = args->paramChart;
8416 XA_PROFILE_START(packChartsAddChartsRestoreTexcoords)
8417 paramChart->restoreTexcoords();
8418 XA_PROFILE_END(packChartsAddChartsRestoreTexcoords)
8419 Mesh *mesh = paramChart->mesh();
8420 Chart *chart = args->chart = XA_NEW(MemTag::Default, Chart);
8421 chart->atlasIndex = -1;
8422 chart->material = 0;
8423 chart->indexCount = mesh->indexCount();
8424 chart->indices = mesh->indices();
8425 chart->parametricArea = mesh->computeParametricArea();
8426 if (chart->parametricArea < kAreaEpsilon) {
8427 // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers.
8428 const Vector2 bounds = paramChart->computeParametricBounds();
8429 chart->parametricArea = bounds.x * bounds.y;
8430 }
8431 chart->surfaceArea = mesh->computeSurfaceArea();
8432 chart->vertices = mesh->texcoords();
8433 chart->vertexCount = mesh->vertexCount();
8434 chart->allowRotate = true;
8435 chart->boundaryEdges = &mesh->boundaryEdges();
8436 // Compute bounding box of chart.
8437 BoundingBox2D &bb = args->boundingBox->get();
8438 bb.clear();
8439 for (uint32_t v = 0; v < chart->vertexCount; v++) {
8440 if (mesh->isBoundaryVertex(v))
8441 bb.appendBoundaryVertex(mesh->texcoord(v));
8442 }
8443 bb.compute(mesh->texcoords(), mesh->vertexCount());
8444 chart->majorAxis = bb.majorAxis;
8445 chart->minorAxis = bb.minorAxis;
8446 chart->minCorner = bb.minCorner;
8447 chart->maxCorner = bb.maxCorner;
8448 XA_PROFILE_END(packChartsAddChartsThread)
8449 }
8450
8451 struct Atlas
8452 {
~Atlasxatlas::internal::pack::Atlas8453 ~Atlas()
8454 {
8455 for (uint32_t i = 0; i < m_atlasImages.size(); i++) {
8456 m_atlasImages[i]->~AtlasImage();
8457 XA_FREE(m_atlasImages[i]);
8458 }
8459 for (uint32_t i = 0; i < m_bitImages.size(); i++) {
8460 m_bitImages[i]->~BitImage();
8461 XA_FREE(m_bitImages[i]);
8462 }
8463 for (uint32_t i = 0; i < m_charts.size(); i++) {
8464 m_charts[i]->~Chart();
8465 XA_FREE(m_charts[i]);
8466 }
8467 }
8468
getWidthxatlas::internal::pack::Atlas8469 uint32_t getWidth() const { return m_width; }
getHeightxatlas::internal::pack::Atlas8470 uint32_t getHeight() const { return m_height; }
getNumAtlasesxatlas::internal::pack::Atlas8471 uint32_t getNumAtlases() const { return m_bitImages.size(); }
getTexelsPerUnitxatlas::internal::pack::Atlas8472 float getTexelsPerUnit() const { return m_texelsPerUnit; }
getChartxatlas::internal::pack::Atlas8473 const Chart *getChart(uint32_t index) const { return m_charts[index]; }
getChartCountxatlas::internal::pack::Atlas8474 uint32_t getChartCount() const { return m_charts.size(); }
getImagesxatlas::internal::pack::Atlas8475 const Array<AtlasImage *> &getImages() const { return m_atlasImages; }
getUtilizationxatlas::internal::pack::Atlas8476 float getUtilization(uint32_t atlas) const { return m_utilization[atlas]; }
8477
addChartsxatlas::internal::pack::Atlas8478 void addCharts(TaskScheduler *taskScheduler, param::Atlas *paramAtlas)
8479 {
8480 // Count charts.
8481 uint32_t chartCount = 0;
8482 for (uint32_t i = 0; i < paramAtlas->meshCount(); i++) {
8483 const uint32_t chartGroupsCount = paramAtlas->chartGroupCount(i);
8484 for (uint32_t j = 0; j < chartGroupsCount; j++) {
8485 const param::ChartGroup *chartGroup = paramAtlas->chartGroupAt(i, j);
8486 chartCount += chartGroup->chartCount();
8487 }
8488 }
8489 if (chartCount == 0)
8490 return;
8491 // Run one task per chart.
8492 Array<AddChartTaskArgs> taskArgs;
8493 taskArgs.resize(chartCount);
8494 TaskGroupHandle taskGroup = taskScheduler->createTaskGroup(chartCount);
8495 uint32_t chartIndex = 0;
8496 ThreadLocal<BoundingBox2D> boundingBox;
8497 for (uint32_t i = 0; i < paramAtlas->meshCount(); i++) {
8498 const uint32_t chartGroupsCount = paramAtlas->chartGroupCount(i);
8499 for (uint32_t j = 0; j < chartGroupsCount; j++) {
8500 const param::ChartGroup *chartGroup = paramAtlas->chartGroupAt(i, j);
8501 const uint32_t count = chartGroup->chartCount();
8502 for (uint32_t k = 0; k < count; k++) {
8503 AddChartTaskArgs &args = taskArgs[chartIndex];
8504 args.boundingBox = &boundingBox;
8505 args.paramChart = chartGroup->chartAt(k);
8506 Task task;
8507 task.userData = &taskArgs[chartIndex];
8508 task.func = runAddChartTask;
8509 taskScheduler->run(taskGroup, task);
8510 chartIndex++;
8511 }
8512 }
8513 }
8514 taskScheduler->wait(&taskGroup);
8515 // Get task output.
8516 m_charts.resize(chartCount);
8517 for (uint32_t i = 0; i < chartCount; i++)
8518 m_charts[i] = taskArgs[i].chart;
8519 }
8520
addUvMeshChartsxatlas::internal::pack::Atlas8521 void addUvMeshCharts(UvMeshInstance *mesh)
8522 {
8523 BitArray vertexUsed(mesh->texcoords.size());
8524 BoundingBox2D boundingBox;
8525 for (uint32_t c = 0; c < mesh->mesh->charts.size(); c++) {
8526 UvMeshChart *uvChart = mesh->mesh->charts[c];
8527 Chart *chart = XA_NEW(MemTag::Default, Chart);
8528 chart->atlasIndex = -1;
8529 chart->material = uvChart->material;
8530 chart->indexCount = uvChart->indices.size();
8531 chart->indices = uvChart->indices.data();
8532 chart->vertices = mesh->texcoords.data();
8533 chart->vertexCount = mesh->texcoords.size();
8534 chart->allowRotate = mesh->rotateCharts;
8535 chart->boundaryEdges = nullptr;
8536 chart->faces.resize(uvChart->faces.size());
8537 memcpy(chart->faces.data(), uvChart->faces.data(), sizeof(uint32_t) * uvChart->faces.size());
8538 // Find unique vertices.
8539 vertexUsed.zeroOutMemory();
8540 for (uint32_t i = 0; i < chart->indexCount; i++) {
8541 const uint32_t vertex = chart->indices[i];
8542 if (!vertexUsed.get(vertex)) {
8543 vertexUsed.set(vertex);
8544 chart->uniqueVertices.push_back(vertex);
8545 }
8546 }
8547 // Compute parametric and surface areas.
8548 chart->parametricArea = 0.0f;
8549 for (uint32_t f = 0; f < chart->indexCount / 3; f++) {
8550 const Vector2 &v1 = chart->vertices[chart->indices[f * 3 + 0]];
8551 const Vector2 &v2 = chart->vertices[chart->indices[f * 3 + 1]];
8552 const Vector2 &v3 = chart->vertices[chart->indices[f * 3 + 2]];
8553 chart->parametricArea += fabsf(triangleArea(v1, v2, v3));
8554 }
8555 chart->parametricArea *= 0.5f;
8556 chart->surfaceArea = chart->parametricArea; // Identical for UV meshes.
8557 if (chart->parametricArea < kAreaEpsilon) {
8558 // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers.
8559 Vector2 minCorner(FLT_MAX, FLT_MAX);
8560 Vector2 maxCorner(-FLT_MAX, -FLT_MAX);
8561 for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
8562 minCorner = min(minCorner, chart->uniqueVertexAt(v));
8563 maxCorner = max(maxCorner, chart->uniqueVertexAt(v));
8564 }
8565 const Vector2 bounds = (maxCorner - minCorner) * 0.5f;
8566 chart->parametricArea = bounds.x * bounds.y;
8567 }
8568 // Compute bounding box of chart.
8569 // Using all unique vertices for simplicity, can compute real boundaries if this is too slow.
8570 boundingBox.clear();
8571 for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++)
8572 boundingBox.appendBoundaryVertex(chart->uniqueVertexAt(v));
8573 boundingBox.compute();
8574 chart->majorAxis = boundingBox.majorAxis;
8575 chart->minorAxis = boundingBox.minorAxis;
8576 chart->minCorner = boundingBox.minCorner;
8577 chart->maxCorner = boundingBox.maxCorner;
8578 m_charts.push_back(chart);
8579 }
8580 }
8581
8582 // Pack charts in the smallest possible rectangle.
packChartsxatlas::internal::pack::Atlas8583 bool packCharts(const PackOptions &options, ProgressFunc progressFunc, void *progressUserData)
8584 {
8585 if (progressFunc) {
8586 if (!progressFunc(ProgressCategory::PackCharts, 0, progressUserData))
8587 return false;
8588 }
8589 const uint32_t chartCount = m_charts.size();
8590 XA_PRINT("Packing %u charts\n", chartCount);
8591 if (chartCount == 0) {
8592 if (progressFunc) {
8593 if (!progressFunc(ProgressCategory::PackCharts, 100, progressUserData))
8594 return false;
8595 }
8596 return true;
8597 }
8598 // Estimate resolution and/or texels per unit if not specified.
8599 m_texelsPerUnit = options.texelsPerUnit;
8600 uint32_t resolution = options.resolution > 0 ? options.resolution + options.padding * 2 : 0;
8601 const uint32_t maxResolution = m_texelsPerUnit > 0.0f ? resolution : 0;
8602 if (resolution <= 0 || m_texelsPerUnit <= 0) {
8603 if (resolution <= 0 && m_texelsPerUnit <= 0)
8604 resolution = 1024;
8605 float meshArea = 0;
8606 for (uint32_t c = 0; c < chartCount; c++)
8607 meshArea += m_charts[c]->surfaceArea;
8608 if (resolution <= 0) {
8609 // Estimate resolution based on the mesh surface area and given texel scale.
8610 const float texelCount = max(1.0f, meshArea * square(m_texelsPerUnit) / 0.75f); // Assume 75% utilization.
8611 resolution = max(1u, nextPowerOfTwo(uint32_t(sqrtf(texelCount))));
8612 }
8613 if (m_texelsPerUnit <= 0) {
8614 // Estimate a suitable texelsPerUnit to fit the given resolution.
8615 const float texelCount = max(1.0f, meshArea / 0.75f); // Assume 75% utilization.
8616 m_texelsPerUnit = sqrtf((resolution * resolution) / texelCount);
8617 XA_PRINT(" Estimating texelsPerUnit as %g\n", m_texelsPerUnit);
8618 }
8619 }
8620 Array<float> chartOrderArray;
8621 chartOrderArray.resize(chartCount);
8622 Array<Vector2> chartExtents;
8623 chartExtents.resize(chartCount);
8624 float minChartPerimeter = FLT_MAX, maxChartPerimeter = 0.0f;
8625 for (uint32_t c = 0; c < chartCount; c++) {
8626 Chart *chart = m_charts[c];
8627 // Compute chart scale
8628 float scale = 1.0f;
8629 if (chart->parametricArea != 0.0f) {
8630 scale = (chart->surfaceArea / chart->parametricArea) * m_texelsPerUnit;
8631 XA_ASSERT(isFinite(scale));
8632 }
8633 // Translate, rotate and scale vertices. Compute extents.
8634 Vector2 minCorner(FLT_MAX, FLT_MAX);
8635 if (!chart->allowRotate) {
8636 for (uint32_t i = 0; i < chart->uniqueVertexCount(); i++)
8637 minCorner = min(minCorner, chart->uniqueVertexAt(i));
8638 }
8639 Vector2 extents(0.0f);
8640 for (uint32_t i = 0; i < chart->uniqueVertexCount(); i++) {
8641 Vector2 &texcoord = chart->uniqueVertexAt(i);
8642 if (chart->allowRotate) {
8643 const float x = dot(texcoord, chart->majorAxis);
8644 const float y = dot(texcoord, chart->minorAxis);
8645 texcoord.x = x;
8646 texcoord.y = y;
8647 texcoord -= chart->minCorner;
8648 } else {
8649 texcoord -= minCorner;
8650 }
8651 texcoord *= scale;
8652 XA_DEBUG_ASSERT(texcoord.x >= 0.0f && texcoord.y >= 0.0f);
8653 XA_DEBUG_ASSERT(isFinite(texcoord.x) && isFinite(texcoord.y));
8654 extents = max(extents, texcoord);
8655 }
8656 XA_DEBUG_ASSERT(extents.x >= 0 && extents.y >= 0);
8657 // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers.
8658 if (extents.x > 0.0f && extents.y > 0.0f) {
8659 // Block align: align all chart extents to 4x4 blocks, but taking padding and texel center offset into account.
8660 const int blockAlignSizeOffset = options.padding * 2 + 1;
8661 int width = ftoi_ceil(extents.x);
8662 if (options.blockAlign)
8663 width = align(width + blockAlignSizeOffset, 4) - blockAlignSizeOffset;
8664 int height = ftoi_ceil(extents.y);
8665 if (options.blockAlign)
8666 height = align(height + blockAlignSizeOffset, 4) - blockAlignSizeOffset;
8667 for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
8668 Vector2 &texcoord = chart->uniqueVertexAt(v);
8669 texcoord.x = texcoord.x / extents.x * (float)width;
8670 texcoord.y = texcoord.y / extents.y * (float)height;
8671 }
8672 extents.x = (float)width;
8673 extents.y = (float)height;
8674 }
8675 // Limit chart size, either to PackOptions::maxChartSize or maxResolution (if set), whichever is smaller.
8676 // If limiting chart size to maxResolution, print a warning, since that may not be desirable to the user.
8677 uint32_t maxChartSize = options.maxChartSize;
8678 bool warnChartResized = false;
8679 if (maxResolution > 0 && (maxChartSize == 0 || maxResolution < maxChartSize)) {
8680 maxChartSize = maxResolution - options.padding * 2; // Don't include padding.
8681 warnChartResized = true;
8682 }
8683 if (maxChartSize > 0) {
8684 const float realMaxChartSize = (float)maxChartSize - 1.0f; // Aligning to texel centers increases texel footprint by 1.
8685 if (extents.x > realMaxChartSize || extents.y > realMaxChartSize) {
8686 if (warnChartResized)
8687 XA_PRINT(" Resizing chart %u from %gx%g to %ux%u to fit atlas\n", c, extents.x, extents.y, maxChartSize, maxChartSize);
8688 scale = realMaxChartSize / max(extents.x, extents.y);
8689 for (uint32_t i = 0; i < chart->uniqueVertexCount(); i++) {
8690 Vector2 &texcoord = chart->uniqueVertexAt(i);
8691 texcoord = min(texcoord * scale, Vector2(realMaxChartSize));
8692 }
8693 }
8694 }
8695 // Align to texel centers and add padding offset.
8696 extents.x = extents.y = 0.0f;
8697 for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
8698 Vector2 &texcoord = chart->uniqueVertexAt(v);
8699 texcoord.x += 0.5f + options.padding;
8700 texcoord.y += 0.5f + options.padding;
8701 extents = max(extents, texcoord);
8702 }
8703 if (extents.x > resolution || extents.y > resolution)
8704 XA_PRINT(" Chart %u extents are large (%gx%g)\n", c, extents.x, extents.y);
8705 chartExtents[c] = extents;
8706 chartOrderArray[c] = extents.x + extents.y; // Use perimeter for chart sort key.
8707 minChartPerimeter = min(minChartPerimeter, chartOrderArray[c]);
8708 maxChartPerimeter = max(maxChartPerimeter, chartOrderArray[c]);
8709 }
8710 // Sort charts by perimeter.
8711 m_radix.sort(chartOrderArray);
8712 const uint32_t *ranks = m_radix.ranks();
8713 // Divide chart perimeter range into buckets.
8714 const float chartPerimeterBucketSize = (maxChartPerimeter - minChartPerimeter) / 16.0f;
8715 uint32_t currentChartBucket = 0;
8716 Array<Vector2i> chartStartPositions; // per atlas
8717 chartStartPositions.push_back(Vector2i(0, 0));
8718 // Pack sorted charts.
8719 #if XA_DEBUG_EXPORT_ATLAS_IMAGES
8720 const bool createImage = true;
8721 #else
8722 const bool createImage = options.createImage;
8723 #endif
8724 // chartImage: result from conservative rasterization
8725 // chartImageBilinear: chartImage plus any texels that would be sampled by bilinear filtering.
8726 // chartImagePadding: either chartImage or chartImageBilinear depending on options, with a dilate filter applied options.padding times.
8727 // Rotated versions swap x and y.
8728 BitImage chartImage, chartImageBilinear, chartImagePadding;
8729 BitImage chartImageRotated, chartImageBilinearRotated, chartImagePaddingRotated;
8730 UniformGrid2 boundaryEdgeGrid;
8731 Array<Vector2i> atlasSizes;
8732 atlasSizes.push_back(Vector2i(0, 0));
8733 int progress = 0;
8734 for (uint32_t i = 0; i < chartCount; i++) {
8735 uint32_t c = ranks[chartCount - i - 1]; // largest chart first
8736 Chart *chart = m_charts[c];
8737 // @@ Add special cases for dot and line charts. @@ Lightmap rasterizer also needs to handle these special cases.
8738 // @@ We could also have a special case for chart quads. If the quad surface <= 4 texels, align vertices with texel centers and do not add padding. May be very useful for foliage.
8739 // @@ In general we could reduce the padding of all charts by one texel by using a rasterizer that takes into account the 2-texel footprint of the tent bilinear filter. For example,
8740 // if we have a chart that is less than 1 texel wide currently we add one texel to the left and one texel to the right creating a 3-texel-wide bitImage. However, if we know that the
8741 // chart is only 1 texel wide we could align it so that it only touches the footprint of two texels:
8742 // | | <- Touches texels 0, 1 and 2.
8743 // | | <- Only touches texels 0 and 1.
8744 // \ \ / \ / /
8745 // \ X X /
8746 // \ / \ / \ /
8747 // V V V
8748 // 0 1 2
8749 XA_PROFILE_START(packChartsRasterize)
8750 // Resize and clear (discard = true) chart images.
8751 // Leave room for padding at extents.
8752 chartImage.resize(ftoi_ceil(chartExtents[c].x) + options.padding, ftoi_ceil(chartExtents[c].y) + options.padding, true);
8753 if (chart->allowRotate)
8754 chartImageRotated.resize(chartImage.height(), chartImage.width(), true);
8755 if (options.bilinear) {
8756 chartImageBilinear.resize(chartImage.width(), chartImage.height(), true);
8757 if (chart->allowRotate)
8758 chartImageBilinearRotated.resize(chartImage.height(), chartImage.width(), true);
8759 }
8760 // Rasterize chart faces.
8761 const uint32_t faceCount = chart->indexCount / 3;
8762 for (uint32_t f = 0; f < faceCount; f++) {
8763 Vector2 vertices[3];
8764 for (uint32_t v = 0; v < 3; v++)
8765 vertices[v] = chart->vertices[chart->indices[f * 3 + v]];
8766 DrawTriangleCallbackArgs args;
8767 args.chartBitImage = &chartImage;
8768 args.chartBitImageRotated = chart->allowRotate ? &chartImageRotated : nullptr;
8769 raster::drawTriangle(Vector2((float)chartImage.width(), (float)chartImage.height()), vertices, drawTriangleCallback, &args);
8770 }
8771 // Expand chart by pixels sampled by bilinear interpolation.
8772 if (options.bilinear)
8773 bilinearExpand(chart, &chartImage, &chartImageBilinear, chart->allowRotate ? &chartImageBilinearRotated : nullptr, boundaryEdgeGrid);
8774 // Expand chart by padding pixels (dilation).
8775 if (options.padding > 0) {
8776 // Copy into the same BitImage instances for every chart to avoid reallocating BitImage buffers (largest chart is packed first).
8777 XA_PROFILE_START(packChartsDilate)
8778 if (options.bilinear)
8779 chartImageBilinear.copyTo(chartImagePadding);
8780 else
8781 chartImage.copyTo(chartImagePadding);
8782 chartImagePadding.dilate(options.padding);
8783 if (chart->allowRotate) {
8784 if (options.bilinear)
8785 chartImageBilinearRotated.copyTo(chartImagePaddingRotated);
8786 else
8787 chartImageRotated.copyTo(chartImagePaddingRotated);
8788 chartImagePaddingRotated.dilate(options.padding);
8789 }
8790 XA_PROFILE_END(packChartsDilate)
8791 }
8792 XA_PROFILE_END(packChartsRasterize)
8793 // Update brute force bucketing.
8794 if (options.bruteForce) {
8795 if (chartOrderArray[c] > minChartPerimeter && chartOrderArray[c] <= maxChartPerimeter - (chartPerimeterBucketSize * (currentChartBucket + 1))) {
8796 // Moved to a smaller bucket, reset start location.
8797 for (uint32_t j = 0; j < chartStartPositions.size(); j++)
8798 chartStartPositions[j] = Vector2i(0, 0);
8799 currentChartBucket++;
8800 }
8801 }
8802 // Find a location to place the chart in the atlas.
8803 BitImage *chartImageToPack, *chartImageToPackRotated;
8804 if (options.padding > 0) {
8805 chartImageToPack = &chartImagePadding;
8806 chartImageToPackRotated = &chartImagePaddingRotated;
8807 } else if (options.bilinear) {
8808 chartImageToPack = &chartImageBilinear;
8809 chartImageToPackRotated = &chartImageBilinearRotated;
8810 } else {
8811 chartImageToPack = &chartImage;
8812 chartImageToPackRotated = &chartImageRotated;
8813 }
8814 uint32_t currentAtlas = 0;
8815 int best_x = 0, best_y = 0;
8816 int best_cw = 0, best_ch = 0;
8817 int best_r = 0;
8818 for (;;)
8819 {
8820 bool firstChartInBitImage = false;
8821 XA_UNUSED(firstChartInBitImage);
8822 if (currentAtlas + 1 > m_bitImages.size()) {
8823 // Chart doesn't fit in the current bitImage, create a new one.
8824 BitImage *bi = XA_NEW_ARGS(MemTag::Default, BitImage, resolution, resolution);
8825 m_bitImages.push_back(bi);
8826 atlasSizes.push_back(Vector2i(0, 0));
8827 firstChartInBitImage = true;
8828 if (createImage)
8829 m_atlasImages.push_back(XA_NEW_ARGS(MemTag::Default, AtlasImage, resolution, resolution));
8830 // Start positions are per-atlas, so create a new one of those too.
8831 chartStartPositions.push_back(Vector2i(0, 0));
8832 }
8833 XA_PROFILE_START(packChartsFindLocation)
8834 const bool foundLocation = findChartLocation(chartStartPositions[currentAtlas], options.bruteForce, m_bitImages[currentAtlas], chartImageToPack, chartImageToPackRotated, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, &best_x, &best_y, &best_cw, &best_ch, &best_r, options.blockAlign, maxResolution, chart->allowRotate);
8835 XA_PROFILE_END(packChartsFindLocation)
8836 XA_DEBUG_ASSERT(!(firstChartInBitImage && !foundLocation)); // Chart doesn't fit in an empty, newly allocated bitImage. Shouldn't happen, since charts are resized if they are too big to fit in the atlas.
8837 if (maxResolution == 0) {
8838 XA_DEBUG_ASSERT(foundLocation); // The atlas isn't limited to a fixed resolution, a chart location should be found on the first attempt.
8839 break;
8840 }
8841 if (foundLocation)
8842 break;
8843 // Chart doesn't fit in the current bitImage, try the next one.
8844 currentAtlas++;
8845 }
8846 // Update brute force start location.
8847 if (options.bruteForce) {
8848 // Reset start location if the chart expanded the atlas.
8849 if (best_x + best_cw > atlasSizes[currentAtlas].x || best_y + best_ch > atlasSizes[currentAtlas].y) {
8850 for (uint32_t j = 0; j < chartStartPositions.size(); j++)
8851 chartStartPositions[j] = Vector2i(0, 0);
8852 }
8853 else {
8854 chartStartPositions[currentAtlas] = Vector2i(best_x, best_y);
8855 }
8856 }
8857 // Update parametric extents.
8858 atlasSizes[currentAtlas].x = max(atlasSizes[currentAtlas].x, best_x + best_cw);
8859 atlasSizes[currentAtlas].y = max(atlasSizes[currentAtlas].y, best_y + best_ch);
8860 // Resize bitImage if necessary.
8861 // If maxResolution > 0, the bitImage is always set to maxResolutionIncludingPadding on creation and doesn't need to be dynamically resized.
8862 if (maxResolution == 0) {
8863 const uint32_t w = (uint32_t)atlasSizes[currentAtlas].x;
8864 const uint32_t h = (uint32_t)atlasSizes[currentAtlas].y;
8865 if (w > m_bitImages[0]->width() || h > m_bitImages[0]->height()) {
8866 m_bitImages[0]->resize(nextPowerOfTwo(w), nextPowerOfTwo(h), false);
8867 if (createImage)
8868 m_atlasImages[0]->resize(m_bitImages[0]->width(), m_bitImages[0]->height());
8869 }
8870 } else {
8871 XA_DEBUG_ASSERT(atlasSizes[currentAtlas].x <= (int)maxResolution);
8872 XA_DEBUG_ASSERT(atlasSizes[currentAtlas].y <= (int)maxResolution);
8873 }
8874 XA_PROFILE_START(packChartsBlit)
8875 addChart(m_bitImages[currentAtlas], chartImageToPack, chartImageToPackRotated, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, best_x, best_y, best_r);
8876 XA_PROFILE_END(packChartsBlit)
8877 if (createImage) {
8878 if (best_r == 0) {
8879 m_atlasImages[currentAtlas]->addChart(c, &chartImage, options.bilinear ? &chartImageBilinear : nullptr, options.padding > 0 ? &chartImagePadding : nullptr, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, best_x, best_y);
8880 } else {
8881 m_atlasImages[currentAtlas]->addChart(c, &chartImageRotated, options.bilinear ? &chartImageBilinearRotated : nullptr, options.padding > 0 ? &chartImagePaddingRotated : nullptr, atlasSizes[currentAtlas].x, atlasSizes[currentAtlas].y, best_x, best_y);
8882 }
8883 #if XA_DEBUG_EXPORT_ATLAS_IMAGES && XA_DEBUG_EXPORT_ATLAS_IMAGES_PER_CHART
8884 for (uint32_t j = 0; j < m_atlasImages.size(); j++) {
8885 char filename[256];
8886 XA_SPRINTF(filename, sizeof(filename), "debug_atlas_image%02u_chart%04u.tga", j, i);
8887 m_atlasImages[j]->writeTga(filename, (uint32_t)atlasSizes[j].x, (uint32_t)atlasSizes[j].y);
8888 }
8889 #endif
8890 }
8891 chart->atlasIndex = (int32_t)currentAtlas;
8892 // Modify texture coordinates:
8893 // - rotate if the chart should be rotated
8894 // - translate to chart location
8895 // - translate to remove padding from top and left atlas edges (unless block aligned)
8896 for (uint32_t v = 0; v < chart->uniqueVertexCount(); v++) {
8897 Vector2 &texcoord = chart->uniqueVertexAt(v);
8898 Vector2 t = texcoord;
8899 if (best_r) {
8900 XA_DEBUG_ASSERT(chart->allowRotate);
8901 swap(t.x, t.y);
8902 }
8903 texcoord.x = best_x + t.x;
8904 texcoord.y = best_y + t.y;
8905 texcoord.x -= (float)options.padding;
8906 texcoord.y -= (float)options.padding;
8907 XA_ASSERT(texcoord.x >= 0 && texcoord.y >= 0);
8908 XA_ASSERT(isFinite(texcoord.x) && isFinite(texcoord.y));
8909 }
8910 if (progressFunc) {
8911 const int newProgress = int((i + 1) / (float)chartCount * 100.0f);
8912 if (newProgress != progress) {
8913 progress = newProgress;
8914 if (!progressFunc(ProgressCategory::PackCharts, progress, progressUserData))
8915 return false;
8916 }
8917 }
8918 }
8919 // Remove padding from outer edges.
8920 if (maxResolution == 0) {
8921 m_width = max(0, atlasSizes[0].x - (int)options.padding * 2);
8922 m_height = max(0, atlasSizes[0].y - (int)options.padding * 2);
8923 } else {
8924 m_width = m_height = maxResolution - (int)options.padding * 2;
8925 }
8926 XA_PRINT(" %dx%d resolution\n", m_width, m_height);
8927 m_utilization.resize(m_bitImages.size());
8928 for (uint32_t i = 0; i < m_utilization.size(); i++) {
8929 if (m_width == 0 || m_height == 0)
8930 m_utilization[i] = 0.0f;
8931 else {
8932 uint32_t count = 0;
8933 for (uint32_t y = 0; y < m_height; y++) {
8934 for (uint32_t x = 0; x < m_width; x++)
8935 count += m_bitImages[i]->get(x, y);
8936 }
8937 m_utilization[i] = float(count) / (m_width * m_height);
8938 }
8939 if (m_utilization.size() > 1) {
8940 XA_PRINT(" %u: %f%% utilization\n", i, m_utilization[i] * 100.0f);
8941 }
8942 else {
8943 XA_PRINT(" %f%% utilization\n", m_utilization[i] * 100.0f);
8944 }
8945 }
8946 #if XA_DEBUG_EXPORT_ATLAS_IMAGES
8947 for (uint32_t i = 0; i < m_atlasImages.size(); i++) {
8948 char filename[256];
8949 XA_SPRINTF(filename, sizeof(filename), "debug_atlas_image%02u.tga", i);
8950 m_atlasImages[i]->writeTga(filename, m_width, m_height);
8951 }
8952 #endif
8953 if (progressFunc && progress != 100) {
8954 if (!progressFunc(ProgressCategory::PackCharts, 100, progressUserData))
8955 return false;
8956 }
8957 return true;
8958 }
8959
8960 private:
8961 // IC: Brute force is slow, and random may take too much time to converge. We start inserting large charts in a small atlas. Using brute force is lame, because most of the space
8962 // is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to
8963 // start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try
8964 // along one axis and then try exhaustively along that axis.
findChartLocationxatlas::internal::pack::Atlas8965 bool findChartLocation(const Vector2i &startPosition, bool bruteForce, const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned, uint32_t maxResolution, bool allowRotate)
8966 {
8967 const int attempts = 4096;
8968 if (bruteForce || attempts >= w * h)
8969 return findChartLocation_bruteForce(startPosition, atlasBitImage, chartBitImage, chartBitImageRotated, w, h, best_x, best_y, best_w, best_h, best_r, blockAligned, maxResolution, allowRotate);
8970 return findChartLocation_random(atlasBitImage, chartBitImage, chartBitImageRotated, w, h, best_x, best_y, best_w, best_h, best_r, attempts, blockAligned, maxResolution, allowRotate);
8971 }
8972
findChartLocation_bruteForcexatlas::internal::pack::Atlas8973 bool findChartLocation_bruteForce(const Vector2i &startPosition, const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, bool blockAligned, uint32_t maxResolution, bool allowRotate)
8974 {
8975 const int stepSize = blockAligned ? 4 : 1;
8976 int best_metric = INT_MAX;
8977 // Try two different orientations.
8978 for (int r = 0; r < 2; r++) {
8979 int cw = chartBitImage->width();
8980 int ch = chartBitImage->height();
8981 if (r == 1) {
8982 if (allowRotate)
8983 swap(cw, ch);
8984 else
8985 break;
8986 }
8987 for (int y = startPosition.y; y <= h + stepSize; y += stepSize) {
8988 if (maxResolution > 0 && y > (int)maxResolution - ch)
8989 break;
8990 for (int x = (y == startPosition.y ? startPosition.x : 0); x <= w + stepSize; x += stepSize) {
8991 if (maxResolution > 0 && x > (int)maxResolution - cw)
8992 break;
8993 // Early out if metric is not better.
8994 const int extentX = max(w, x + cw), extentY = max(h, y + ch);
8995 const int area = extentX * extentY;
8996 const int extents = max(extentX, extentY);
8997 const int metric = extents * extents + area;
8998 if (metric > best_metric)
8999 continue;
9000 // If metric is the same, pick the one closest to the origin.
9001 if (metric == best_metric && max(x, y) >= max(*best_x, *best_y))
9002 continue;
9003 if (!atlasBitImage->canBlit(r == 1 ? *chartBitImageRotated : *chartBitImage, x, y))
9004 continue;
9005 best_metric = metric;
9006 *best_x = x;
9007 *best_y = y;
9008 *best_w = cw;
9009 *best_h = ch;
9010 *best_r = r;
9011 if (area == w * h)
9012 return true; // Chart is completely inside, do not look at any other location.
9013 }
9014 }
9015 }
9016 return best_metric != INT_MAX;
9017 }
9018
findChartLocation_randomxatlas::internal::pack::Atlas9019 bool findChartLocation_random(const BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int w, int h, int *best_x, int *best_y, int *best_w, int *best_h, int *best_r, int minTrialCount, bool blockAligned, uint32_t maxResolution, bool allowRotate)
9020 {
9021 bool result = false;
9022 const int BLOCK_SIZE = 4;
9023 int best_metric = INT_MAX;
9024 for (int i = 0; i < minTrialCount; i++) {
9025 int cw = chartBitImage->width();
9026 int ch = chartBitImage->height();
9027 int r = allowRotate ? m_rand.getRange(1) : 0;
9028 if (r == 1)
9029 swap(cw, ch);
9030 // + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas.
9031 int xRange = w + 1;
9032 int yRange = h + 1;
9033 // Clamp to max resolution.
9034 if (maxResolution > 0) {
9035 xRange = min(xRange, (int)maxResolution - cw);
9036 yRange = min(yRange, (int)maxResolution - ch);
9037 }
9038 int x = m_rand.getRange(xRange);
9039 int y = m_rand.getRange(yRange);
9040 if (blockAligned) {
9041 x = align(x, BLOCK_SIZE);
9042 y = align(y, BLOCK_SIZE);
9043 if (maxResolution > 0 && (x > (int)maxResolution - cw || y > (int)maxResolution - ch))
9044 continue; // Block alignment pushed the chart outside the atlas.
9045 }
9046 // Early out.
9047 int area = max(w, x + cw) * max(h, y + ch);
9048 //int perimeter = max(w, x+cw) + max(h, y+ch);
9049 int extents = max(max(w, x + cw), max(h, y + ch));
9050 int metric = extents * extents + area;
9051 if (metric > best_metric) {
9052 continue;
9053 }
9054 if (metric == best_metric && min(x, y) > min(*best_x, *best_y)) {
9055 // If metric is the same, pick the one closest to the origin.
9056 continue;
9057 }
9058 if (atlasBitImage->canBlit(r == 1 ? *chartBitImageRotated : *chartBitImage, x, y)) {
9059 result = true;
9060 best_metric = metric;
9061 *best_x = x;
9062 *best_y = y;
9063 *best_w = cw;
9064 *best_h = ch;
9065 *best_r = allowRotate ? r : 0;
9066 if (area == w * h) {
9067 // Chart is completely inside, do not look at any other location.
9068 break;
9069 }
9070 }
9071 }
9072 return result;
9073 }
9074
addChartxatlas::internal::pack::Atlas9075 void addChart(BitImage *atlasBitImage, const BitImage *chartBitImage, const BitImage *chartBitImageRotated, int atlas_w, int atlas_h, int offset_x, int offset_y, int r)
9076 {
9077 XA_DEBUG_ASSERT(r == 0 || r == 1);
9078 const BitImage *image = r == 0 ? chartBitImage : chartBitImageRotated;
9079 const int w = image->width();
9080 const int h = image->height();
9081 for (int y = 0; y < h; y++) {
9082 int yy = y + offset_y;
9083 if (yy >= 0) {
9084 for (int x = 0; x < w; x++) {
9085 int xx = x + offset_x;
9086 if (xx >= 0) {
9087 if (image->get(x, y)) {
9088 if (xx < atlas_w && yy < atlas_h) {
9089 XA_DEBUG_ASSERT(atlasBitImage->get(xx, yy) == false);
9090 atlasBitImage->set(xx, yy);
9091 }
9092 }
9093 }
9094 }
9095 }
9096 }
9097 }
9098
bilinearExpandxatlas::internal::pack::Atlas9099 void bilinearExpand(const Chart *chart, BitImage *source, BitImage *dest, BitImage *destRotated, UniformGrid2 &boundaryEdgeGrid) const
9100 {
9101 boundaryEdgeGrid.reset(chart->vertices, chart->indices);
9102 if (chart->boundaryEdges) {
9103 const uint32_t edgeCount = chart->boundaryEdges->size();
9104 for (uint32_t i = 0; i < edgeCount; i++)
9105 boundaryEdgeGrid.append((*chart->boundaryEdges)[i]);
9106 } else {
9107 for (uint32_t i = 0; i < chart->indexCount; i++)
9108 boundaryEdgeGrid.append(i);
9109 }
9110 const int xOffsets[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
9111 const int yOffsets[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
9112 for (uint32_t y = 0; y < source->height(); y++) {
9113 for (uint32_t x = 0; x < source->width(); x++) {
9114 // Copy pixels from source.
9115 if (source->get(x, y))
9116 goto setPixel;
9117 // Empty pixel. If none of of the surrounding pixels are set, this pixel can't be sampled by bilinear interpolation.
9118 {
9119 uint32_t s = 0;
9120 for (; s < 8; s++) {
9121 const int sx = (int)x + xOffsets[s];
9122 const int sy = (int)y + yOffsets[s];
9123 if (sx < 0 || sy < 0 || sx >= (int)source->width() || sy >= (int)source->height())
9124 continue;
9125 if (source->get((uint32_t)sx, (uint32_t)sy))
9126 break;
9127 }
9128 if (s == 8)
9129 continue;
9130 }
9131 {
9132 // If a 2x2 square centered on the pixels centroid intersects the triangle, this pixel will be sampled by bilinear interpolation.
9133 // See "Precomputed Global Illumination in Frostbite (GDC 2018)" page 95
9134 const Vector2 centroid((float)x + 0.5f, (float)y + 0.5f);
9135 const Vector2 squareVertices[4] = {
9136 Vector2(centroid.x - 1.0f, centroid.y - 1.0f),
9137 Vector2(centroid.x + 1.0f, centroid.y - 1.0f),
9138 Vector2(centroid.x + 1.0f, centroid.y + 1.0f),
9139 Vector2(centroid.x - 1.0f, centroid.y + 1.0f)
9140 };
9141 for (uint32_t j = 0; j < 4; j++) {
9142 if (boundaryEdgeGrid.intersect(squareVertices[j], squareVertices[(j + 1) % 4], 0.0f))
9143 goto setPixel;
9144 }
9145 }
9146 continue;
9147 setPixel:
9148 dest->set(x, y);
9149 if (destRotated)
9150 destRotated->set(y, x);
9151 }
9152 }
9153 }
9154
9155 struct DrawTriangleCallbackArgs
9156 {
9157 BitImage *chartBitImage, *chartBitImageRotated;
9158 };
9159
drawTriangleCallbackxatlas::internal::pack::Atlas9160 static bool drawTriangleCallback(void *param, int x, int y)
9161 {
9162 auto args = (DrawTriangleCallbackArgs *)param;
9163 args->chartBitImage->set(x, y);
9164 if (args->chartBitImageRotated)
9165 args->chartBitImageRotated->set(y, x);
9166 return true;
9167 }
9168
9169 Array<AtlasImage *> m_atlasImages;
9170 Array<float> m_utilization;
9171 Array<BitImage *> m_bitImages;
9172 Array<Chart *> m_charts;
9173 RadixSort m_radix;
9174 uint32_t m_width = 0;
9175 uint32_t m_height = 0;
9176 float m_texelsPerUnit = 0.0f;
9177 KISSRng m_rand;
9178 };
9179
9180 } // namespace pack
9181 } // namespace internal
9182
9183 struct Context
9184 {
9185 Atlas atlas;
9186 internal::Progress *addMeshProgress = nullptr;
9187 internal::TaskGroupHandle addMeshTaskGroup;
9188 internal::param::Atlas paramAtlas;
9189 ProgressFunc progressFunc = nullptr;
9190 void *progressUserData = nullptr;
9191 internal::TaskScheduler *taskScheduler;
9192 internal::Array<internal::Mesh *> meshes;
9193 internal::Array<internal::UvMesh *> uvMeshes;
9194 internal::Array<internal::UvMeshInstance *> uvMeshInstances;
9195 };
9196
Create()9197 Atlas *Create()
9198 {
9199 Context *ctx = XA_NEW(internal::MemTag::Default, Context);
9200 memset(&ctx->atlas, 0, sizeof(Atlas));
9201 ctx->taskScheduler = XA_NEW(internal::MemTag::Default, internal::TaskScheduler);
9202 return &ctx->atlas;
9203 }
9204
DestroyOutputMeshes(Context * ctx)9205 static void DestroyOutputMeshes(Context *ctx)
9206 {
9207 if (!ctx->atlas.meshes)
9208 return;
9209 for (int i = 0; i < (int)ctx->atlas.meshCount; i++) {
9210 Mesh &mesh = ctx->atlas.meshes[i];
9211 if (mesh.chartArray) {
9212 for (uint32_t j = 0; j < mesh.chartCount; j++) {
9213 if (mesh.chartArray[j].faceArray)
9214 XA_FREE(mesh.chartArray[j].faceArray);
9215 }
9216 XA_FREE(mesh.chartArray);
9217 }
9218 if (mesh.vertexArray)
9219 XA_FREE(mesh.vertexArray);
9220 if (mesh.indexArray)
9221 XA_FREE(mesh.indexArray);
9222 }
9223 XA_FREE(ctx->atlas.meshes);
9224 ctx->atlas.meshes = nullptr;
9225 }
9226
Destroy(Atlas * atlas)9227 void Destroy(Atlas *atlas)
9228 {
9229 XA_DEBUG_ASSERT(atlas);
9230 Context *ctx = (Context *)atlas;
9231 if (atlas->utilization)
9232 XA_FREE(atlas->utilization);
9233 if (atlas->image)
9234 XA_FREE(atlas->image);
9235 DestroyOutputMeshes(ctx);
9236 if (ctx->addMeshProgress) {
9237 ctx->addMeshProgress->cancel = true;
9238 AddMeshJoin(atlas); // frees addMeshProgress
9239 }
9240 ctx->taskScheduler->~TaskScheduler();
9241 XA_FREE(ctx->taskScheduler);
9242 for (uint32_t i = 0; i < ctx->meshes.size(); i++) {
9243 internal::Mesh *mesh = ctx->meshes[i];
9244 mesh->~Mesh();
9245 XA_FREE(mesh);
9246 }
9247 for (uint32_t i = 0; i < ctx->uvMeshes.size(); i++) {
9248 internal::UvMesh *mesh = ctx->uvMeshes[i];
9249 for (uint32_t j = 0; j < mesh->charts.size(); j++) {
9250 mesh->charts[j]->~UvMeshChart();
9251 XA_FREE(mesh->charts[j]);
9252 }
9253 mesh->~UvMesh();
9254 XA_FREE(mesh);
9255 }
9256 for (uint32_t i = 0; i < ctx->uvMeshInstances.size(); i++) {
9257 internal::UvMeshInstance *mesh = ctx->uvMeshInstances[i];
9258 mesh->~UvMeshInstance();
9259 XA_FREE(mesh);
9260 }
9261 ctx->~Context();
9262 XA_FREE(ctx);
9263 #if XA_DEBUG_HEAP
9264 internal::ReportLeaks();
9265 #endif
9266 }
9267
9268 struct AddMeshTaskArgs
9269 {
9270 Context *ctx;
9271 internal::Mesh *mesh;
9272 };
9273
runAddMeshTask(void * userData)9274 static void runAddMeshTask(void *userData)
9275 {
9276 XA_PROFILE_START(addMeshThread)
9277 auto args = (AddMeshTaskArgs *)userData; // Responsible for freeing this.
9278 internal::Mesh *mesh = args->mesh;
9279 internal::Progress *progress = args->ctx->addMeshProgress;
9280 if (progress->cancel)
9281 goto cleanup;
9282 {
9283 XA_PROFILE_START(addMeshCreateColocals)
9284 mesh->createColocals();
9285 XA_PROFILE_END(addMeshCreateColocals)
9286 }
9287 if (progress->cancel)
9288 goto cleanup;
9289 progress->value++;
9290 progress->update();
9291 cleanup:
9292 args->~AddMeshTaskArgs();
9293 XA_FREE(args);
9294 XA_PROFILE_END(addMeshThread)
9295 }
9296
DecodePosition(const MeshDecl & meshDecl,uint32_t index)9297 static internal::Vector3 DecodePosition(const MeshDecl &meshDecl, uint32_t index)
9298 {
9299 XA_DEBUG_ASSERT(meshDecl.vertexPositionData);
9300 XA_DEBUG_ASSERT(meshDecl.vertexPositionStride > 0);
9301 return *((const internal::Vector3 *)&((const uint8_t *)meshDecl.vertexPositionData)[meshDecl.vertexPositionStride * index]);
9302 }
9303
DecodeNormal(const MeshDecl & meshDecl,uint32_t index)9304 static internal::Vector3 DecodeNormal(const MeshDecl &meshDecl, uint32_t index)
9305 {
9306 XA_DEBUG_ASSERT(meshDecl.vertexNormalData);
9307 XA_DEBUG_ASSERT(meshDecl.vertexNormalStride > 0);
9308 return *((const internal::Vector3 *)&((const uint8_t *)meshDecl.vertexNormalData)[meshDecl.vertexNormalStride * index]);
9309 }
9310
DecodeUv(const MeshDecl & meshDecl,uint32_t index)9311 static internal::Vector2 DecodeUv(const MeshDecl &meshDecl, uint32_t index)
9312 {
9313 XA_DEBUG_ASSERT(meshDecl.vertexUvData);
9314 XA_DEBUG_ASSERT(meshDecl.vertexUvStride > 0);
9315 return *((const internal::Vector2 *)&((const uint8_t *)meshDecl.vertexUvData)[meshDecl.vertexUvStride * index]);
9316 }
9317
DecodeIndex(IndexFormat::Enum format,const void * indexData,int32_t offset,uint32_t i)9318 static uint32_t DecodeIndex(IndexFormat::Enum format, const void *indexData, int32_t offset, uint32_t i)
9319 {
9320 XA_DEBUG_ASSERT(indexData);
9321 if (format == IndexFormat::UInt16)
9322 return uint16_t((int32_t)((const uint16_t *)indexData)[i] + offset);
9323 return uint32_t((int32_t)((const uint32_t *)indexData)[i] + offset);
9324 }
9325
AddMesh(Atlas * atlas,const MeshDecl & meshDecl,uint32_t meshCountHint)9326 AddMeshError::Enum AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t meshCountHint)
9327 {
9328 XA_DEBUG_ASSERT(atlas);
9329 if (!atlas) {
9330 XA_PRINT_WARNING("AddMesh: atlas is null.\n");
9331 return AddMeshError::Error;
9332 }
9333 Context *ctx = (Context *)atlas;
9334 if (!ctx->uvMeshes.isEmpty()) {
9335 XA_PRINT_WARNING("AddMesh: Meshes and UV meshes cannot be added to the same atlas.\n");
9336 return AddMeshError::Error;
9337 }
9338 #if XA_PROFILE
9339 if (ctx->meshes.isEmpty())
9340 internal::s_profile.addMeshReal = clock();
9341 #endif
9342 // Don't know how many times AddMesh will be called, so progress needs to adjusted each time.
9343 if (!ctx->addMeshProgress) {
9344 ctx->addMeshProgress = XA_NEW_ARGS(internal::MemTag::Default, internal::Progress, ProgressCategory::AddMesh, ctx->progressFunc, ctx->progressUserData, 1);
9345 }
9346 else {
9347 ctx->addMeshProgress->setMaxValue(internal::max(ctx->meshes.size() + 1, meshCountHint));
9348 }
9349 XA_PROFILE_START(addMeshCopyData)
9350 const bool hasIndices = meshDecl.indexCount > 0;
9351 const uint32_t indexCount = hasIndices ? meshDecl.indexCount : meshDecl.vertexCount;
9352 XA_PRINT("Adding mesh %d: %u vertices, %u triangles\n", ctx->meshes.size(), meshDecl.vertexCount, indexCount / 3);
9353 // Expecting triangle faces.
9354 if ((indexCount % 3) != 0)
9355 return AddMeshError::InvalidIndexCount;
9356 if (hasIndices) {
9357 // Check if any index is out of range.
9358 for (uint32_t i = 0; i < indexCount; i++) {
9359 const uint32_t index = DecodeIndex(meshDecl.indexFormat, meshDecl.indexData, meshDecl.indexOffset, i);
9360 if (index >= meshDecl.vertexCount)
9361 return AddMeshError::IndexOutOfRange;
9362 }
9363 }
9364 uint32_t meshFlags = internal::MeshFlags::HasIgnoredFaces;
9365 if (meshDecl.vertexNormalData)
9366 meshFlags |= internal::MeshFlags::HasNormals;
9367 internal::Mesh *mesh = XA_NEW_ARGS(internal::MemTag::Mesh, internal::Mesh, meshDecl.epsilon, meshDecl.vertexCount, indexCount / 3, meshFlags, ctx->meshes.size());
9368 for (uint32_t i = 0; i < meshDecl.vertexCount; i++) {
9369 internal::Vector3 normal(0.0f);
9370 internal::Vector2 texcoord(0.0f);
9371 if (meshDecl.vertexNormalData)
9372 normal = DecodeNormal(meshDecl, i);
9373 if (meshDecl.vertexUvData)
9374 texcoord = DecodeUv(meshDecl, i);
9375 mesh->addVertex(DecodePosition(meshDecl, i), normal, texcoord);
9376 }
9377 const uint32_t kMaxWarnings = 50;
9378 uint32_t warningCount = 0;
9379 for (uint32_t i = 0; i < indexCount / 3; i++) {
9380 uint32_t tri[3];
9381 for (int j = 0; j < 3; j++)
9382 tri[j] = hasIndices ? DecodeIndex(meshDecl.indexFormat, meshDecl.indexData, meshDecl.indexOffset, i * 3 + j) : i * 3 + j;
9383 bool ignore = false;
9384 // Check for degenerate or zero length edges.
9385 for (int j = 0; j < 3; j++) {
9386 const uint32_t index1 = tri[j];
9387 const uint32_t index2 = tri[(j + 1) % 3];
9388 if (index1 == index2) {
9389 ignore = true;
9390 if (++warningCount <= kMaxWarnings)
9391 XA_PRINT(" Degenerate edge: index %d, index %d\n", index1, index2);
9392 break;
9393 }
9394 const internal::Vector3 &pos1 = mesh->position(index1);
9395 const internal::Vector3 &pos2 = mesh->position(index2);
9396 if (internal::length(pos2 - pos1) <= 0.0f) {
9397 ignore = true;
9398 if (++warningCount <= kMaxWarnings)
9399 XA_PRINT(" Zero length edge: index %d position (%g %g %g), index %d position (%g %g %g)\n", index1, pos1.x, pos1.y, pos1.z, index2, pos2.x, pos2.y, pos2.z);
9400 break;
9401 }
9402 }
9403 // Ignore faces with any nan vertex attributes.
9404 if (!ignore) {
9405 for (int j = 0; j < 3; j++) {
9406 const internal::Vector3 &pos = mesh->position(tri[j]);
9407 if (internal::isNan(pos.x) || internal::isNan(pos.y) || internal::isNan(pos.z)) {
9408 if (++warningCount <= kMaxWarnings)
9409 XA_PRINT(" NAN position in face: %d\n", i);
9410 ignore = true;
9411 break;
9412 }
9413 if (meshDecl.vertexNormalData) {
9414 const internal::Vector3 &normal = mesh->normal(tri[j]);
9415 if (internal::isNan(normal.x) || internal::isNan(normal.y) || internal::isNan(normal.z)) {
9416 if (++warningCount <= kMaxWarnings)
9417 XA_PRINT(" NAN normal in face: %d\n", i);
9418 ignore = true;
9419 break;
9420 }
9421 }
9422 if (meshDecl.vertexUvData) {
9423 const internal::Vector2 &uv = mesh->texcoord(tri[j]);
9424 if (internal::isNan(uv.x) || internal::isNan(uv.y)) {
9425 if (++warningCount <= kMaxWarnings)
9426 XA_PRINT(" NAN texture coordinate in face: %d\n", i);
9427 ignore = true;
9428 break;
9429 }
9430 }
9431 }
9432 }
9433 const internal::Vector3 &a = mesh->position(tri[0]);
9434 const internal::Vector3 &b = mesh->position(tri[1]);
9435 const internal::Vector3 &c = mesh->position(tri[2]);
9436 // Check for zero area faces.
9437 float area = 0.0f;
9438 if (!ignore) {
9439 area = internal::length(internal::cross(b - a, c - a)) * 0.5f;
9440 if (area <= internal::kAreaEpsilon) {
9441 ignore = true;
9442 if (++warningCount <= kMaxWarnings)
9443 XA_PRINT(" Zero area face: %d, indices (%d %d %d), area is %f\n", i, tri[0], tri[1], tri[2], area);
9444 }
9445 }
9446 if (!ignore) {
9447 if (internal::equal(a, b, meshDecl.epsilon) || internal::equal(a, c, meshDecl.epsilon) || internal::equal(b, c, meshDecl.epsilon)) {
9448 ignore = true;
9449 if (++warningCount <= kMaxWarnings)
9450 XA_PRINT(" Degenerate face: %d, area is %f\n", i, area);
9451 }
9452 }
9453 if (meshDecl.faceIgnoreData && meshDecl.faceIgnoreData[i])
9454 ignore = true;
9455 mesh->addFace(tri[0], tri[1], tri[2], ignore);
9456 }
9457 if (warningCount > kMaxWarnings)
9458 XA_PRINT(" %u additional warnings truncated\n", warningCount - kMaxWarnings);
9459 XA_PROFILE_END(addMeshCopyData)
9460 ctx->meshes.push_back(mesh);
9461 ctx->paramAtlas.addMesh(mesh);
9462 if (ctx->addMeshTaskGroup.value == UINT32_MAX)
9463 ctx->addMeshTaskGroup = ctx->taskScheduler->createTaskGroup();
9464 AddMeshTaskArgs *taskArgs = XA_NEW(internal::MemTag::Default, AddMeshTaskArgs); // The task frees this.
9465 taskArgs->ctx = ctx;
9466 taskArgs->mesh = mesh;
9467 internal::Task task;
9468 task.userData = taskArgs;
9469 task.func = runAddMeshTask;
9470 ctx->taskScheduler->run(ctx->addMeshTaskGroup, task);
9471 return AddMeshError::Success;
9472 }
9473
AddMeshJoin(Atlas * atlas)9474 void AddMeshJoin(Atlas *atlas)
9475 {
9476 XA_DEBUG_ASSERT(atlas);
9477 if (!atlas) {
9478 XA_PRINT_WARNING("AddMeshJoin: atlas is null.\n");
9479 return;
9480 }
9481 Context *ctx = (Context *)atlas;
9482 if (!ctx->addMeshProgress)
9483 return;
9484 ctx->taskScheduler->wait(&ctx->addMeshTaskGroup);
9485 ctx->addMeshProgress->~Progress();
9486 XA_FREE(ctx->addMeshProgress);
9487 ctx->addMeshProgress = nullptr;
9488 #if XA_PROFILE
9489 XA_PRINT("Added %u meshes\n", ctx->meshes.size());
9490 internal::s_profile.addMeshReal = clock() - internal::s_profile.addMeshReal;
9491 #endif
9492 XA_PROFILE_PRINT_AND_RESET(" Total (real): ", addMeshReal)
9493 XA_PROFILE_PRINT_AND_RESET(" Copy data: ", addMeshCopyData)
9494 XA_PROFILE_PRINT_AND_RESET(" Total (thread): ", addMeshThread)
9495 XA_PROFILE_PRINT_AND_RESET(" Create colocals: ", addMeshCreateColocals)
9496 #if XA_PROFILE_ALLOC
9497 XA_PROFILE_PRINT_AND_RESET(" Alloc: ", alloc)
9498 #endif
9499 XA_PRINT_MEM_USAGE
9500 #if XA_DEBUG_EXPORT_OBJ_FACE_GROUPS
9501 internal::param::s_faceGroupsCurrentVertex = 0;
9502 #endif
9503 }
9504
9505 struct EdgeKey
9506 {
EdgeKeyxatlas::EdgeKey9507 EdgeKey() {}
EdgeKeyxatlas::EdgeKey9508 EdgeKey(const EdgeKey &k) : v0(k.v0), v1(k.v1) {}
EdgeKeyxatlas::EdgeKey9509 EdgeKey(uint32_t v0, uint32_t v1) : v0(v0), v1(v1) {}
operator ==xatlas::EdgeKey9510 bool operator==(const EdgeKey &k) const { return v0 == k.v0 && v1 == k.v1; }
9511
9512 uint32_t v0;
9513 uint32_t v1;
9514 };
9515
AddUvMesh(Atlas * atlas,const UvMeshDecl & decl)9516 AddMeshError::Enum AddUvMesh(Atlas *atlas, const UvMeshDecl &decl)
9517 {
9518 XA_DEBUG_ASSERT(atlas);
9519 if (!atlas) {
9520 XA_PRINT_WARNING("AddUvMesh: atlas is null.\n");
9521 return AddMeshError::Error;
9522 }
9523 Context *ctx = (Context *)atlas;
9524 if (!ctx->meshes.isEmpty()) {
9525 XA_PRINT_WARNING("AddUvMesh: Meshes and UV meshes cannot be added to the same atlas.\n");
9526 return AddMeshError::Error;
9527 }
9528 const bool decoded = (decl.indexCount <= 0);
9529 const uint32_t indexCount = decoded ? decl.vertexCount : decl.indexCount;
9530 XA_PRINT("Adding UV mesh %d: %u vertices, %u triangles\n", ctx->uvMeshes.size(), decl.vertexCount, indexCount / 3);
9531 // Expecting triangle faces.
9532 if ((indexCount % 3) != 0)
9533 return AddMeshError::InvalidIndexCount;
9534 if (!decoded) {
9535 // Check if any index is out of range.
9536 for (uint32_t i = 0; i < indexCount; i++) {
9537 const uint32_t index = DecodeIndex(decl.indexFormat, decl.indexData, decl.indexOffset, i);
9538 if (index >= decl.vertexCount)
9539 return AddMeshError::IndexOutOfRange;
9540 }
9541 }
9542 internal::UvMeshInstance *meshInstance = XA_NEW(internal::MemTag::Default, internal::UvMeshInstance);
9543 meshInstance->texcoords.resize(decl.vertexCount);
9544 for (uint32_t i = 0; i < decl.vertexCount; i++) {
9545 internal::Vector2 texcoord = *((const internal::Vector2 *)&((const uint8_t *)decl.vertexUvData)[decl.vertexStride * i]);
9546 // Set nan values to 0.
9547 if (internal::isNan(texcoord.x) || internal::isNan(texcoord.y))
9548 texcoord.x = texcoord.y = 0.0f;
9549 meshInstance->texcoords[i] = texcoord;
9550 }
9551 meshInstance->rotateCharts = decl.rotateCharts;
9552 // See if this is an instance of an already existing mesh.
9553 internal::UvMesh *mesh = nullptr;
9554 for (uint32_t m = 0; m < ctx->uvMeshes.size(); m++) {
9555 if (memcmp(&ctx->uvMeshes[m]->decl, &decl, sizeof(UvMeshDecl)) == 0) {
9556 meshInstance->mesh = mesh = ctx->uvMeshes[m];
9557 break;
9558 }
9559 }
9560 if (!mesh) {
9561 // Copy geometry to mesh.
9562 meshInstance->mesh = mesh = XA_NEW(internal::MemTag::Default, internal::UvMesh);
9563 mesh->decl = decl;
9564 mesh->indices.resize(decl.indexCount);
9565 for (uint32_t i = 0; i < indexCount; i++)
9566 mesh->indices[i] = decoded ? i : DecodeIndex(decl.indexFormat, decl.indexData, decl.indexOffset, i);
9567 mesh->vertexToChartMap.resize(decl.vertexCount);
9568 for (uint32_t i = 0; i < mesh->vertexToChartMap.size(); i++)
9569 mesh->vertexToChartMap[i] = UINT32_MAX;
9570 // Calculate charts (incident faces).
9571 internal::HashMap<internal::Vector2> vertexToFaceMap(internal::MemTag::Default, indexCount); // Face is index / 3
9572 const uint32_t faceCount = indexCount / 3;
9573 for (uint32_t i = 0; i < indexCount; i++)
9574 vertexToFaceMap.add(meshInstance->texcoords[mesh->indices[i]]);
9575 internal::BitArray faceAssigned(faceCount);
9576 faceAssigned.zeroOutMemory();
9577 for (uint32_t f = 0; f < faceCount; f++) {
9578 if (faceAssigned.get(f))
9579 continue;
9580 // Found an unassigned face, create a new chart.
9581 internal::UvMeshChart *chart = XA_NEW(internal::MemTag::Default, internal::UvMeshChart);
9582 chart->material = decl.faceMaterialData ? decl.faceMaterialData[f] : 0;
9583 // Walk incident faces and assign them to the chart.
9584 faceAssigned.set(f);
9585 chart->faces.push_back(f);
9586 for (;;) {
9587 bool newFaceAssigned = false;
9588 const uint32_t faceCount2 = chart->faces.size();
9589 for (uint32_t f2 = 0; f2 < faceCount2; f2++) {
9590 const uint32_t face = chart->faces[f2];
9591 for (uint32_t i = 0; i < 3; i++) {
9592 const internal::Vector2 &texcoord = meshInstance->texcoords[meshInstance->mesh->indices[face * 3 + i]];
9593 uint32_t mapIndex = vertexToFaceMap.get(texcoord);
9594 while (mapIndex != UINT32_MAX) {
9595 const uint32_t face2 = mapIndex / 3; // 3 vertices added per face.
9596 // Materials must match.
9597 if (!faceAssigned.get(face2) && (!decl.faceMaterialData || decl.faceMaterialData[face] == decl.faceMaterialData[face2])) {
9598 faceAssigned.set(face2);
9599 chart->faces.push_back(face2);
9600 newFaceAssigned = true;
9601 }
9602 mapIndex = vertexToFaceMap.getNext(mapIndex);
9603 }
9604 }
9605 }
9606 if (!newFaceAssigned)
9607 break;
9608 }
9609 for (uint32_t i = 0; i < chart->faces.size(); i++) {
9610 for (uint32_t j = 0; j < 3; j++) {
9611 const uint32_t vertex = meshInstance->mesh->indices[chart->faces[i] * 3 + j];
9612 chart->indices.push_back(vertex);
9613 mesh->vertexToChartMap[vertex] = mesh->charts.size();
9614 }
9615 }
9616 mesh->charts.push_back(chart);
9617 }
9618 ctx->uvMeshes.push_back(mesh);
9619 } else {
9620 XA_PRINT(" instance of a previous UV mesh\n");
9621 }
9622 XA_PRINT(" %u charts\n", meshInstance->mesh->charts.size());
9623 ctx->uvMeshInstances.push_back(meshInstance);
9624 return AddMeshError::Success;
9625 }
9626
ComputeCharts(Atlas * atlas,ChartOptions options)9627 void ComputeCharts(Atlas *atlas, ChartOptions options)
9628 {
9629 if (!atlas) {
9630 XA_PRINT_WARNING("ComputeCharts: atlas is null.\n");
9631 return;
9632 }
9633 Context *ctx = (Context *)atlas;
9634 if (!ctx->uvMeshInstances.isEmpty()) {
9635 XA_PRINT_WARNING("ComputeCharts: This function should not be called with UV meshes.\n");
9636 return;
9637 }
9638 AddMeshJoin(atlas);
9639 if (ctx->meshes.isEmpty()) {
9640 XA_PRINT_WARNING("ComputeCharts: No meshes. Call AddMesh first.\n");
9641 return;
9642 }
9643 XA_PRINT("Computing charts\n");
9644 XA_PROFILE_START(computeChartsReal)
9645 if (!ctx->paramAtlas.computeCharts(ctx->taskScheduler, options, ctx->progressFunc, ctx->progressUserData)) {
9646 XA_PRINT(" Cancelled by user\n");
9647 return;
9648 }
9649 XA_PROFILE_END(computeChartsReal)
9650 // Count charts.
9651 uint32_t chartCount = 0;
9652 const uint32_t meshCount = ctx->meshes.size();
9653 for (uint32_t i = 0; i < meshCount; i++) {
9654 for (uint32_t j = 0; j < ctx->paramAtlas.chartGroupCount(i); j++) {
9655 const internal::param::ChartGroup *chartGroup = ctx->paramAtlas.chartGroupAt(i, j);
9656 chartCount += chartGroup->segmentChartCount();
9657 }
9658 }
9659 XA_PRINT(" %u charts\n", chartCount);
9660 #if XA_PROFILE
9661 XA_PRINT(" Chart groups\n");
9662 uint32_t chartGroupCount = 0;
9663 for (uint32_t i = 0; i < meshCount; i++) {
9664 XA_PRINT(" Mesh %u: %u chart groups\n", i, ctx->paramAtlas.chartGroupCount(i));
9665 chartGroupCount += ctx->paramAtlas.chartGroupCount(i);
9666 }
9667 XA_PRINT(" %u total\n", chartGroupCount);
9668 #endif
9669 XA_PROFILE_PRINT_AND_RESET(" Total (real): ", computeChartsReal)
9670 XA_PROFILE_PRINT_AND_RESET(" Total (thread): ", computeChartsThread)
9671 XA_PROFILE_PRINT_AND_RESET(" Create face groups: ", createFaceGroups)
9672 XA_PROFILE_PRINT_AND_RESET(" Extract invalid mesh geometry: ", extractInvalidMeshGeometry)
9673 XA_PROFILE_PRINT_AND_RESET(" Chart group compute charts (real): ", chartGroupComputeChartsReal)
9674 XA_PROFILE_PRINT_AND_RESET(" Chart group compute charts (thread): ", chartGroupComputeChartsThread)
9675 XA_PROFILE_PRINT_AND_RESET(" Create chart group mesh: ", createChartGroupMesh)
9676 XA_PROFILE_PRINT_AND_RESET(" Create colocals: ", createChartGroupMeshColocals)
9677 XA_PROFILE_PRINT_AND_RESET(" Create boundaries: ", createChartGroupMeshBoundaries)
9678 XA_PROFILE_PRINT_AND_RESET(" Build atlas: ", buildAtlas)
9679 XA_PROFILE_PRINT_AND_RESET(" Init: ", buildAtlasInit)
9680 XA_PROFILE_PRINT_AND_RESET(" Planar charts: ", planarCharts)
9681 XA_PROFILE_PRINT_AND_RESET(" Clustered charts: ", clusteredCharts)
9682 XA_PROFILE_PRINT_AND_RESET(" Place seeds: ", clusteredChartsPlaceSeeds)
9683 XA_PROFILE_PRINT_AND_RESET(" Boundary intersection: ", clusteredChartsPlaceSeedsBoundaryIntersection)
9684 XA_PROFILE_PRINT_AND_RESET(" Relocate seeds: ", clusteredChartsRelocateSeeds)
9685 XA_PROFILE_PRINT_AND_RESET(" Reset: ", clusteredChartsReset)
9686 XA_PROFILE_PRINT_AND_RESET(" Grow: ", clusteredChartsGrow)
9687 XA_PROFILE_PRINT_AND_RESET(" Boundary intersection: ", clusteredChartsGrowBoundaryIntersection)
9688 XA_PROFILE_PRINT_AND_RESET(" Merge: ", clusteredChartsMerge)
9689 XA_PROFILE_PRINT_AND_RESET(" Fill holes: ", clusteredChartsFillHoles)
9690 XA_PROFILE_PRINT_AND_RESET(" Copy chart faces: ", copyChartFaces)
9691 #if XA_PROFILE_ALLOC
9692 XA_PROFILE_PRINT_AND_RESET(" Alloc: ", alloc)
9693 #endif
9694 XA_PRINT_MEM_USAGE
9695 }
9696
ParameterizeCharts(Atlas * atlas,ParameterizeOptions options)9697 void ParameterizeCharts(Atlas *atlas, ParameterizeOptions options)
9698 {
9699 if (!atlas) {
9700 XA_PRINT_WARNING("ParameterizeCharts: atlas is null.\n");
9701 return;
9702 }
9703 Context *ctx = (Context *)atlas;
9704 if (!ctx->uvMeshInstances.isEmpty()) {
9705 XA_PRINT_WARNING("ParameterizeCharts: This function should not be called with UV meshes.\n");
9706 return;
9707 }
9708 if (!ctx->paramAtlas.chartsComputed()) {
9709 XA_PRINT_WARNING("ParameterizeCharts: ComputeCharts must be called first.\n");
9710 return;
9711 }
9712 atlas->atlasCount = 0;
9713 atlas->height = 0;
9714 atlas->texelsPerUnit = 0;
9715 atlas->width = 0;
9716 if (atlas->utilization) {
9717 XA_FREE(atlas->utilization);
9718 atlas->utilization = nullptr;
9719 }
9720 if (atlas->image) {
9721 XA_FREE(atlas->image);
9722 atlas->image = nullptr;
9723 }
9724 DestroyOutputMeshes(ctx);
9725 XA_PRINT("Parameterizing charts\n");
9726 XA_PROFILE_START(parameterizeChartsReal)
9727 if (!ctx->paramAtlas.parameterizeCharts(ctx->taskScheduler, options, ctx->progressFunc, ctx->progressUserData)) {
9728 XA_PRINT(" Cancelled by user\n");
9729 return;
9730 }
9731 XA_PROFILE_END(parameterizeChartsReal)
9732 const uint32_t meshCount = ctx->meshes.size();
9733 uint32_t chartCount = 0, chartsWithHolesCount = 0, holesCount = 0, chartsWithTJunctionsCount = 0, tJunctionsCount = 0, orthoChartsCount = 0, planarChartsCount = 0, lscmChartsCount = 0, piecewiseChartsCount = 0, chartsAddedCount = 0, chartsDeletedCount = 0;
9734 for (uint32_t i = 0; i < meshCount; i++) {
9735 for (uint32_t j = 0; j < ctx->paramAtlas.chartGroupCount(i); j++) {
9736 const internal::param::ChartGroup *chartGroup = ctx->paramAtlas.chartGroupAt(i, j);
9737 for (uint32_t k = 0; k < chartGroup->chartCount(); k++) {
9738 const internal::param::Chart *chart = chartGroup->chartAt(k);
9739 #if XA_PRINT_CHART_WARNINGS
9740 if (chart->warningFlags() & internal::param::ChartWarningFlags::CloseHolesFailed)
9741 XA_PRINT_WARNING(" Chart %u (mesh %u, group %u, id %u): failed to close holes\n", chartCount, i, j, k);
9742 if (chart->warningFlags() & internal::param::ChartWarningFlags::FixTJunctionsDuplicatedEdge)
9743 XA_PRINT_WARNING(" Chart %u (mesh %u, group %u, id %u): fixing t-junctions created non-manifold geometry\n", chartCount, i, j, k);
9744 if (chart->warningFlags() & internal::param::ChartWarningFlags::FixTJunctionsFailed)
9745 XA_PRINT_WARNING(" Chart %u (mesh %u, group %u, id %u): fixing t-junctions failed\n", chartCount, i, j, k);
9746 if (chart->warningFlags() & internal::param::ChartWarningFlags::TriangulateDuplicatedEdge)
9747 XA_PRINT_WARNING(" Chart %u (mesh %u, group %u, id %u): triangulation created non-manifold geometry\n", chartCount, i, j, k);
9748 #endif
9749 holesCount += chart->closedHolesCount();
9750 if (chart->closedHolesCount() > 0)
9751 chartsWithHolesCount++;
9752 tJunctionsCount += chart->fixedTJunctionsCount();
9753 if (chart->fixedTJunctionsCount() > 0)
9754 chartsWithTJunctionsCount++;
9755 if (chart->type() == ChartType::Planar)
9756 planarChartsCount++;
9757 else if (chart->type() == ChartType::Ortho)
9758 orthoChartsCount++;
9759 else if (chart->type() == ChartType::LSCM)
9760 lscmChartsCount++;
9761 else if (chart->type() == ChartType::Piecewise)
9762 piecewiseChartsCount++;
9763 }
9764 chartCount += chartGroup->chartCount();
9765 chartsAddedCount += chartGroup->paramAddedChartsCount();
9766 chartsDeletedCount += chartGroup->paramDeletedChartsCount();
9767 }
9768 }
9769 if (holesCount > 0)
9770 XA_PRINT(" %u holes closed in %u charts\n", holesCount, chartsWithHolesCount);
9771 if (tJunctionsCount > 0)
9772 XA_PRINT(" %u t-junctions fixed in %u charts\n", tJunctionsCount, chartsWithTJunctionsCount);
9773 XA_PRINT(" %u planar charts, %u ortho charts, %u LSCM charts, %u piecewise charts\n", planarChartsCount, orthoChartsCount, lscmChartsCount, piecewiseChartsCount);
9774 if (chartsDeletedCount > 0) {
9775 XA_PRINT(" %u charts with invalid parameterizations replaced with %u new charts\n", chartsDeletedCount, chartsAddedCount);
9776 XA_PRINT(" %u charts\n", chartCount);
9777 }
9778 uint32_t chartIndex = 0, invalidParamCount = 0;
9779 for (uint32_t i = 0; i < meshCount; i++) {
9780 for (uint32_t j = 0; j < ctx->paramAtlas.chartGroupCount(i); j++) {
9781 const internal::param::ChartGroup *chartGroup = ctx->paramAtlas.chartGroupAt(i, j);
9782 for (uint32_t k = 0; k < chartGroup->chartCount(); k++) {
9783 internal::param::Chart *chart = chartGroup->chartAt(k);
9784 const internal::param::Quality &quality = chart->quality();
9785 #if XA_DEBUG_EXPORT_OBJ_CHARTS_AFTER_PARAMETERIZATION
9786 {
9787 char filename[256];
9788 XA_SPRINTF(filename, sizeof(filename), "debug_chart_%03u_after_parameterization.obj", chartIndex);
9789 chart->unifiedMesh()->writeObjFile(filename);
9790 }
9791 #endif
9792 const char *type = "LSCM";
9793 if (chart->type() == ChartType::Planar)
9794 type = "planar";
9795 else if (chart->type() == ChartType::Ortho)
9796 type = "ortho";
9797 else if (chart->type() == ChartType::Piecewise)
9798 type = "piecewise";
9799 if (chart->isInvalid()) {
9800 if (quality.boundaryIntersection) {
9801 XA_PRINT_WARNING(" Chart %u (mesh %u, group %u, id %u) (%s): invalid parameterization, self-intersecting boundary.\n", chartIndex, i, j, k, type);
9802 }
9803 if (quality.flippedTriangleCount > 0) {
9804 XA_PRINT_WARNING(" Chart %u (mesh %u, group %u, id %u) (%s): invalid parameterization, %u / %u flipped triangles.\n", chartIndex, i, j, k, type, quality.flippedTriangleCount, quality.totalTriangleCount);
9805 }
9806 invalidParamCount++;
9807 #if XA_DEBUG_EXPORT_OBJ_INVALID_PARAMETERIZATION
9808 char filename[256];
9809 XA_SPRINTF(filename, sizeof(filename), "debug_chart_%03u_invalid_parameterization.obj", chartIndex);
9810 const internal::Mesh *mesh = chart->unifiedMesh();
9811 FILE *file;
9812 XA_FOPEN(file, filename, "w");
9813 if (file) {
9814 mesh->writeObjVertices(file);
9815 fprintf(file, "s off\n");
9816 fprintf(file, "o object\n");
9817 for (uint32_t f = 0; f < mesh->faceCount(); f++)
9818 mesh->writeObjFace(file, f);
9819 if (!chart->paramFlippedFaces().isEmpty()) {
9820 fprintf(file, "o flipped_faces\n");
9821 for (uint32_t f = 0; f < chart->paramFlippedFaces().size(); f++)
9822 mesh->writeObjFace(file, chart->paramFlippedFaces()[f]);
9823 }
9824 mesh->writeObjBoundaryEges(file);
9825 mesh->writeObjLinkedBoundaries(file);
9826 fclose(file);
9827 }
9828 #endif
9829 }
9830 chartIndex++;
9831 }
9832 }
9833 }
9834 if (invalidParamCount > 0)
9835 XA_PRINT_WARNING(" %u charts with invalid parameterizations\n", invalidParamCount);
9836 XA_PROFILE_PRINT_AND_RESET(" Total (real): ", parameterizeChartsReal)
9837 XA_PROFILE_PRINT_AND_RESET(" Total (thread): ", parameterizeChartsThread)
9838 XA_PROFILE_PRINT_AND_RESET(" Create chart mesh: ", createChartMesh)
9839 XA_PROFILE_PRINT_AND_RESET(" Fix t-junctions: ", fixChartMeshTJunctions)
9840 XA_PROFILE_PRINT_AND_RESET(" Close holes: ", closeChartMeshHoles)
9841 XA_PROFILE_PRINT_AND_RESET(" Orthogonal: ", parameterizeChartsOrthogonal)
9842 XA_PROFILE_PRINT_AND_RESET(" LSCM: ", parameterizeChartsLSCM)
9843 XA_PROFILE_PRINT_AND_RESET(" Recompute: ", parameterizeChartsRecompute)
9844 XA_PROFILE_PRINT_AND_RESET(" Piecewise: ", parameterizeChartsPiecewise)
9845 XA_PROFILE_PRINT_AND_RESET(" Boundary intersection: ", parameterizeChartsPiecewiseBoundaryIntersection)
9846 XA_PROFILE_PRINT_AND_RESET(" Evaluate quality: ", parameterizeChartsEvaluateQuality)
9847 #if XA_PROFILE_ALLOC
9848 XA_PROFILE_PRINT_AND_RESET(" Alloc: ", alloc)
9849 #endif
9850 XA_PRINT_MEM_USAGE
9851 }
9852
PackCharts(Atlas * atlas,PackOptions packOptions)9853 void PackCharts(Atlas *atlas, PackOptions packOptions)
9854 {
9855 // Validate arguments and context state.
9856 if (!atlas) {
9857 XA_PRINT_WARNING("PackCharts: atlas is null.\n");
9858 return;
9859 }
9860 Context *ctx = (Context *)atlas;
9861 if (ctx->meshes.isEmpty() && ctx->uvMeshInstances.isEmpty()) {
9862 XA_PRINT_WARNING("PackCharts: No meshes. Call AddMesh or AddUvMesh first.\n");
9863 return;
9864 }
9865 if (ctx->uvMeshInstances.isEmpty()) {
9866 if (!ctx->paramAtlas.chartsComputed()) {
9867 XA_PRINT_WARNING("PackCharts: ComputeCharts must be called first.\n");
9868 return;
9869 }
9870 if (!ctx->paramAtlas.chartsParameterized()) {
9871 XA_PRINT_WARNING("PackCharts: ParameterizeCharts must be called first.\n");
9872 return;
9873 }
9874 }
9875 if (packOptions.texelsPerUnit < 0.0f) {
9876 XA_PRINT_WARNING("PackCharts: PackOptions::texelsPerUnit is negative.\n");
9877 packOptions.texelsPerUnit = 0.0f;
9878 }
9879 // Cleanup atlas.
9880 DestroyOutputMeshes(ctx);
9881 if (atlas->utilization) {
9882 XA_FREE(atlas->utilization);
9883 atlas->utilization = nullptr;
9884 }
9885 if (atlas->image) {
9886 XA_FREE(atlas->image);
9887 atlas->image = nullptr;
9888 }
9889 atlas->meshCount = 0;
9890 // Pack charts.
9891 XA_PROFILE_START(packChartsAddCharts)
9892 internal::pack::Atlas packAtlas;
9893 if (!ctx->uvMeshInstances.isEmpty()) {
9894 for (uint32_t i = 0; i < ctx->uvMeshInstances.size(); i++)
9895 packAtlas.addUvMeshCharts(ctx->uvMeshInstances[i]);
9896 }
9897 else
9898 packAtlas.addCharts(ctx->taskScheduler, &ctx->paramAtlas);
9899 XA_PROFILE_END(packChartsAddCharts)
9900 XA_PROFILE_START(packCharts)
9901 if (!packAtlas.packCharts(packOptions, ctx->progressFunc, ctx->progressUserData))
9902 return;
9903 XA_PROFILE_END(packCharts)
9904 // Populate atlas object with pack results.
9905 atlas->atlasCount = packAtlas.getNumAtlases();
9906 atlas->chartCount = packAtlas.getChartCount();
9907 atlas->width = packAtlas.getWidth();
9908 atlas->height = packAtlas.getHeight();
9909 atlas->texelsPerUnit = packAtlas.getTexelsPerUnit();
9910 if (atlas->atlasCount > 0) {
9911 atlas->utilization = XA_ALLOC_ARRAY(internal::MemTag::Default, float, atlas->atlasCount);
9912 for (uint32_t i = 0; i < atlas->atlasCount; i++)
9913 atlas->utilization[i] = packAtlas.getUtilization(i);
9914 }
9915 if (packOptions.createImage) {
9916 atlas->image = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, atlas->atlasCount * atlas->width * atlas->height);
9917 for (uint32_t i = 0; i < atlas->atlasCount; i++)
9918 packAtlas.getImages()[i]->copyTo(&atlas->image[atlas->width * atlas->height * i], atlas->width, atlas->height, packOptions.padding);
9919 }
9920 XA_PROFILE_PRINT_AND_RESET(" Total: ", packCharts)
9921 XA_PROFILE_PRINT_AND_RESET(" Add charts (real): ", packChartsAddCharts)
9922 XA_PROFILE_PRINT_AND_RESET(" Add charts (thread): ", packChartsAddChartsThread)
9923 XA_PROFILE_PRINT_AND_RESET(" Restore texcoords: ", packChartsAddChartsRestoreTexcoords)
9924 XA_PROFILE_PRINT_AND_RESET(" Rasterize: ", packChartsRasterize)
9925 XA_PROFILE_PRINT_AND_RESET(" Dilate (padding): ", packChartsDilate)
9926 XA_PROFILE_PRINT_AND_RESET(" Find location: ", packChartsFindLocation)
9927 XA_PROFILE_PRINT_AND_RESET(" Blit: ", packChartsBlit)
9928 #if XA_PROFILE_ALLOC
9929 XA_PROFILE_PRINT_AND_RESET(" Alloc: ", alloc)
9930 #endif
9931 XA_PRINT_MEM_USAGE
9932 XA_PRINT("Building output meshes\n");
9933 XA_PROFILE_START(buildOutputMeshes)
9934 int progress = 0;
9935 if (ctx->progressFunc) {
9936 if (!ctx->progressFunc(ProgressCategory::BuildOutputMeshes, 0, ctx->progressUserData))
9937 return;
9938 }
9939 if (ctx->uvMeshInstances.isEmpty())
9940 atlas->meshCount = ctx->meshes.size();
9941 else
9942 atlas->meshCount = ctx->uvMeshInstances.size();
9943 atlas->meshes = XA_ALLOC_ARRAY(internal::MemTag::Default, Mesh, atlas->meshCount);
9944 memset(atlas->meshes, 0, sizeof(Mesh) * atlas->meshCount);
9945 if (ctx->uvMeshInstances.isEmpty()) {
9946 uint32_t chartIndex = 0;
9947 for (uint32_t i = 0; i < atlas->meshCount; i++) {
9948 Mesh &outputMesh = atlas->meshes[i];
9949 // Count and alloc arrays.
9950 const internal::param::InvalidMeshGeometry &invalid = ctx->paramAtlas.invalidMeshGeometry(i);
9951 outputMesh.vertexCount += invalid.vertices().length;
9952 outputMesh.indexCount += invalid.faces().length * 3;
9953 for (uint32_t cg = 0; cg < ctx->paramAtlas.chartGroupCount(i); cg++) {
9954 const internal::param::ChartGroup *chartGroup = ctx->paramAtlas.chartGroupAt(i, cg);
9955 for (uint32_t c = 0; c < chartGroup->chartCount(); c++) {
9956 const internal::param::Chart *chart = chartGroup->chartAt(c);
9957 outputMesh.vertexCount += chart->mesh()->vertexCount();
9958 outputMesh.indexCount += chart->mesh()->faceCount() * 3;
9959 outputMesh.chartCount++;
9960 }
9961 }
9962 outputMesh.vertexArray = XA_ALLOC_ARRAY(internal::MemTag::Default, Vertex, outputMesh.vertexCount);
9963 outputMesh.indexArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputMesh.indexCount);
9964 outputMesh.chartArray = XA_ALLOC_ARRAY(internal::MemTag::Default, Chart, outputMesh.chartCount);
9965 XA_PRINT(" Mesh %u: %u vertices, %u triangles, %u charts\n", i, outputMesh.vertexCount, outputMesh.indexCount / 3, outputMesh.chartCount);
9966 // Copy mesh data.
9967 uint32_t firstVertex = 0;
9968 {
9969 const internal::param::InvalidMeshGeometry &mesh = ctx->paramAtlas.invalidMeshGeometry(i);
9970 internal::ConstArrayView<uint32_t> faces = mesh.faces();
9971 internal::ConstArrayView<uint32_t> indices = mesh.indices();
9972 internal::ConstArrayView<uint32_t> vertices = mesh.vertices();
9973 // Vertices.
9974 for (uint32_t v = 0; v < vertices.length; v++) {
9975 Vertex &vertex = outputMesh.vertexArray[v];
9976 vertex.atlasIndex = -1;
9977 vertex.chartIndex = -1;
9978 vertex.uv[0] = vertex.uv[1] = 0.0f;
9979 vertex.xref = vertices[v];
9980 }
9981 // Indices.
9982 for (uint32_t f = 0; f < faces.length; f++) {
9983 const uint32_t indexOffset = faces[f] * 3;
9984 for (uint32_t j = 0; j < 3; j++)
9985 outputMesh.indexArray[indexOffset + j] = indices[f * 3 + j];
9986 }
9987 firstVertex = vertices.length;
9988 }
9989 uint32_t meshChartIndex = 0;
9990 for (uint32_t cg = 0; cg < ctx->paramAtlas.chartGroupCount(i); cg++) {
9991 const internal::param::ChartGroup *chartGroup = ctx->paramAtlas.chartGroupAt(i, cg);
9992 for (uint32_t c = 0; c < chartGroup->chartCount(); c++) {
9993 const internal::param::Chart *chart = chartGroup->chartAt(c);
9994 const internal::Mesh *mesh = chart->mesh();
9995 // Vertices.
9996 for (uint32_t v = 0; v < mesh->vertexCount(); v++) {
9997 Vertex &vertex = outputMesh.vertexArray[firstVertex + v];
9998 vertex.atlasIndex = packAtlas.getChart(chartIndex)->atlasIndex;
9999 XA_DEBUG_ASSERT(vertex.atlasIndex >= 0);
10000 vertex.chartIndex = (int32_t)chartIndex;
10001 const internal::Vector2 &uv = mesh->texcoord(v);
10002 vertex.uv[0] = internal::max(0.0f, uv.x);
10003 vertex.uv[1] = internal::max(0.0f, uv.y);
10004 vertex.xref = chart->mapChartVertexToSourceVertex(v);
10005 }
10006 // Indices.
10007 for (uint32_t f = 0; f < mesh->faceCount(); f++) {
10008 const uint32_t indexOffset = chart->mapFaceToSourceFace(f) * 3;
10009 for (uint32_t j = 0; j < 3; j++)
10010 outputMesh.indexArray[indexOffset + j] = firstVertex + mesh->vertexAt(f * 3 + j);
10011 }
10012 // Charts.
10013 Chart *outputChart = &outputMesh.chartArray[meshChartIndex];
10014 const int32_t atlasIndex = packAtlas.getChart(chartIndex)->atlasIndex;
10015 XA_DEBUG_ASSERT(atlasIndex >= 0);
10016 outputChart->atlasIndex = (uint32_t)atlasIndex;
10017 outputChart->type = chart->isInvalid() ? ChartType::Invalid : chart->type();
10018 outputChart->faceCount = mesh->faceCount();
10019 outputChart->faceArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputChart->faceCount);
10020 for (uint32_t f = 0; f < outputChart->faceCount; f++)
10021 outputChart->faceArray[f] = chart->mapFaceToSourceFace(f);
10022 outputChart->material = 0;
10023 meshChartIndex++;
10024 chartIndex++;
10025 firstVertex += mesh->vertexCount();
10026 }
10027 }
10028 XA_DEBUG_ASSERT(outputMesh.vertexCount == firstVertex);
10029 XA_DEBUG_ASSERT(outputMesh.chartCount == meshChartIndex);
10030 if (ctx->progressFunc) {
10031 const int newProgress = int((i + 1) / (float)atlas->meshCount * 100.0f);
10032 if (newProgress != progress) {
10033 progress = newProgress;
10034 if (!ctx->progressFunc(ProgressCategory::BuildOutputMeshes, progress, ctx->progressUserData))
10035 return;
10036 }
10037 }
10038 }
10039 } else {
10040 uint32_t chartIndex = 0;
10041 for (uint32_t m = 0; m < ctx->uvMeshInstances.size(); m++) {
10042 Mesh &outputMesh = atlas->meshes[m];
10043 const internal::UvMeshInstance *mesh = ctx->uvMeshInstances[m];
10044 // Alloc arrays.
10045 outputMesh.vertexCount = mesh->texcoords.size();
10046 outputMesh.indexCount = mesh->mesh->indices.size();
10047 outputMesh.chartCount = mesh->mesh->charts.size();
10048 outputMesh.vertexArray = XA_ALLOC_ARRAY(internal::MemTag::Default, Vertex, outputMesh.vertexCount);
10049 outputMesh.indexArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputMesh.indexCount);
10050 outputMesh.chartArray = XA_ALLOC_ARRAY(internal::MemTag::Default, Chart, outputMesh.chartCount);
10051 XA_PRINT(" UV mesh %u: %u vertices, %u triangles, %u charts\n", m, outputMesh.vertexCount, outputMesh.indexCount / 3, outputMesh.chartCount);
10052 // Copy mesh data.
10053 // Vertices.
10054 for (uint32_t v = 0; v < mesh->texcoords.size(); v++) {
10055 Vertex &vertex = outputMesh.vertexArray[v];
10056 vertex.uv[0] = mesh->texcoords[v].x;
10057 vertex.uv[1] = mesh->texcoords[v].y;
10058 vertex.xref = v;
10059 const uint32_t meshChartIndex = mesh->mesh->vertexToChartMap[v];
10060 if (meshChartIndex == UINT32_MAX) {
10061 // Vertex doesn't exist in any chart.
10062 vertex.atlasIndex = -1;
10063 vertex.chartIndex = -1;
10064 } else {
10065 const internal::pack::Chart *chart = packAtlas.getChart(chartIndex + meshChartIndex);
10066 vertex.atlasIndex = chart->atlasIndex;
10067 vertex.chartIndex = (int32_t)chartIndex + meshChartIndex;
10068 }
10069 }
10070 // Indices.
10071 memcpy(outputMesh.indexArray, mesh->mesh->indices.data(), mesh->mesh->indices.size() * sizeof(uint32_t));
10072 // Charts.
10073 for (uint32_t c = 0; c < mesh->mesh->charts.size(); c++) {
10074 Chart *outputChart = &outputMesh.chartArray[c];
10075 const internal::pack::Chart *chart = packAtlas.getChart(chartIndex);
10076 XA_DEBUG_ASSERT(chart->atlasIndex >= 0);
10077 outputChart->atlasIndex = (uint32_t)chart->atlasIndex;
10078 outputChart->faceCount = chart->faces.size();
10079 outputChart->faceArray = XA_ALLOC_ARRAY(internal::MemTag::Default, uint32_t, outputChart->faceCount);
10080 outputChart->material = chart->material;
10081 for (uint32_t f = 0; f < outputChart->faceCount; f++)
10082 outputChart->faceArray[f] = chart->faces[f];
10083 chartIndex++;
10084 }
10085 if (ctx->progressFunc) {
10086 const int newProgress = int((m + 1) / (float)atlas->meshCount * 100.0f);
10087 if (newProgress != progress) {
10088 progress = newProgress;
10089 if (!ctx->progressFunc(ProgressCategory::BuildOutputMeshes, progress, ctx->progressUserData))
10090 return;
10091 }
10092 }
10093 }
10094 }
10095 if (ctx->progressFunc && progress != 100)
10096 ctx->progressFunc(ProgressCategory::BuildOutputMeshes, 100, ctx->progressUserData);
10097 XA_PROFILE_END(buildOutputMeshes)
10098 XA_PROFILE_PRINT_AND_RESET(" Total: ", buildOutputMeshes)
10099 #if XA_PROFILE_ALLOC
10100 XA_PROFILE_PRINT_AND_RESET(" Alloc: ", alloc)
10101 #endif
10102 XA_PRINT_MEM_USAGE
10103 }
10104
Generate(Atlas * atlas,ChartOptions chartOptions,ParameterizeOptions parameterizeOptions,PackOptions packOptions)10105 void Generate(Atlas *atlas, ChartOptions chartOptions, ParameterizeOptions parameterizeOptions, PackOptions packOptions)
10106 {
10107 if (!atlas) {
10108 XA_PRINT_WARNING("Generate: atlas is null.\n");
10109 return;
10110 }
10111 Context *ctx = (Context *)atlas;
10112 if (!ctx->uvMeshInstances.isEmpty()) {
10113 XA_PRINT_WARNING("Generate: This function should not be called with UV meshes.\n");
10114 return;
10115 }
10116 if (ctx->meshes.isEmpty()) {
10117 XA_PRINT_WARNING("Generate: No meshes. Call AddMesh first.\n");
10118 return;
10119 }
10120 ComputeCharts(atlas, chartOptions);
10121 ParameterizeCharts(atlas, parameterizeOptions);
10122 PackCharts(atlas, packOptions);
10123 }
10124
SetProgressCallback(Atlas * atlas,ProgressFunc progressFunc,void * progressUserData)10125 void SetProgressCallback(Atlas *atlas, ProgressFunc progressFunc, void *progressUserData)
10126 {
10127 if (!atlas) {
10128 XA_PRINT_WARNING("SetProgressCallback: atlas is null.\n");
10129 return;
10130 }
10131 Context *ctx = (Context *)atlas;
10132 ctx->progressFunc = progressFunc;
10133 ctx->progressUserData = progressUserData;
10134 }
10135
SetAlloc(ReallocFunc reallocFunc,FreeFunc freeFunc)10136 void SetAlloc(ReallocFunc reallocFunc, FreeFunc freeFunc)
10137 {
10138 internal::s_realloc = reallocFunc;
10139 internal::s_free = freeFunc;
10140 }
10141
SetPrint(PrintFunc print,bool verbose)10142 void SetPrint(PrintFunc print, bool verbose)
10143 {
10144 internal::s_print = print;
10145 internal::s_printVerbose = verbose;
10146 }
10147
StringForEnum(AddMeshError::Enum error)10148 const char *StringForEnum(AddMeshError::Enum error)
10149 {
10150 if (error == AddMeshError::Error)
10151 return "Unspecified error";
10152 if (error == AddMeshError::IndexOutOfRange)
10153 return "Index out of range";
10154 if (error == AddMeshError::InvalidIndexCount)
10155 return "Invalid index count";
10156 return "Success";
10157 }
10158
StringForEnum(ProgressCategory::Enum category)10159 const char *StringForEnum(ProgressCategory::Enum category)
10160 {
10161 if (category == ProgressCategory::AddMesh)
10162 return "Adding mesh(es)";
10163 if (category == ProgressCategory::ComputeCharts)
10164 return "Computing charts";
10165 if (category == ProgressCategory::ParameterizeCharts)
10166 return "Parameterizing charts";
10167 if (category == ProgressCategory::PackCharts)
10168 return "Packing charts";
10169 if (category == ProgressCategory::BuildOutputMeshes)
10170 return "Building output meshes";
10171 return "";
10172 }
10173
10174 } // namespace xatlas
10175