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 ((&centroid.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> &paramFlippedFaces() 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