diff --git a/README.md b/README.md
index 110697ce..3f22b602 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,156 @@
-CUDA Path Tracer
-================
+**University of Pennsylvania, CIS 565: GPU Programming and Architecture**
+# Project 3 - CUDA Path Tracer
-**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 3**
+* Jonas Oppenheim ([LinkedIn](https://www.linkedin.com/in/jonasoppenheim/), [GitHub](https://github.com/oppenheimj/), [personal](http://www.jonasoppenheim.com/))
+* Tested on: Windows 10, Ryzen 9 5950x, 32GB, RTX 3080 (personal machine)
-* (TODO) YOUR NAME HERE
-* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
+
+
+Image 1: Abduction A
+
-### (TODO: Your README)
+
+
+Image 2: Abduction B
+
-*DO NOT* leave the README to the last minute! It is a crucial part of the
-project, and we will not be able to grade you without a good README.
+## Introduction
+In the simulation that we call reality, photon [waves/particles](https://en.wikipedia.org/wiki/Double-slit_experiment) flow from light sources and bounce around the physical world. If we're interested in generating photo-realistic images by modeling this behavior of light, then it is most efficient to only model the few photons that reach our eyeballs. This is the idea behind path tracing. The procedure is as follows. Every iteration, fire one ray into the scene for every pixel. Allow each of these rays to bounce around the scene a certain number of times (depth). At every bounce, change the ray's color and direction based on the material it hit. If a ray hits a light source, stop bouncing it. So there is a notion of _iterations_, which is the number of times a set of rays is shot into the scene, and of _depth_, which is how many bounces are permitted per ray per iteraton. Multiple iterations are required because for a given pixel, the ray's path from iteration to iteration will likely be different because rays do not reflect from most materials deterministically. The following three images show a section of pixels after 1, 10, and 100 iterations. It is seen that over time, each pixel converges to its "correct" color.
+
+
+
+The following image shows the variety of features implemented in this project. The next section covers qualitative and quantitative descriptions of these features.
+
+
+
+Image 3: Feature list
+
+
+## Description and analysis of features
+
+
+
+Image 4: Test image for stream compaction, material sorting, and first-bounce caching
+
+
+### Stream compaction
+The purpose of stream compaction is to efficiently remove elements matching a particlar criteria from an array. Stream compaction is essentially high performacne filtering. This is useful in the context of path tracing because if a ray has zero bounces left, then that ray no longer needs to be processed.
+
+Stream compaction performance analysis was test on Image 3 using `thrust::stable_partition`. The canvas was 800x800 pixels and the bounce depth was eight. Invocations of both the `computeIntersections` and `shadeMaterial` kernels were timed for each bounce of each iteration between iteratons 30 and 50, which warmed up the cache and smoothed the results.
+
+Within a particular iteration, rays bounce around up to a certain number of times, but are terminated early if they don't hit anything. The expectation was that removing these early terminated rays would result in smaller and therefore faster kernel invocations. Indeed, Figure 1 shows that kernels run faster with stream compaction and Figure 2 shows why: later bounces have fewer and fewer rays.
+
+It is worth mentioning that stream compaction itself took approximately 13,000 microseconds, which is far longer than it took any of the kernels to run. For a scene the size of the one tested, there is a net performance loss. For a larger image, though, there improvement in kernel performance is likely to outweigh the cost of running stream compaction every bounce.
+
+
+
+Figure 1
+
+
+
+
+Figure 2
+
+
+### Material sorting
+The idea behind material sorting is to sort our rays and intersections by the material that was hit. This way, threads within warps are more likely to take similar logical paths through kernel conditionals and there will be fewer warp divergences. Figure 3 shows that indeed, when material sorting is enabled, performance improves for both kernels.
+
+It is obvious why sorting by material made the shadeMaterials kernel run faster. The shadeMaterials kernel represents the BSDF that has a bunch of if-else statements depending on the material type. But within an iteration, the material sorting actually happens _after_ computeIntersections, so it was surprising to see such an improvement. I believe that running computeIntersections during subsequent bounces benefits for the same reason, namely that rays that hit the same material are likely to intersect with similar objects later in the scene.
+
+It is noteworthy that running `thrust::stable_sort_by_key` takes 50,167 microseconds, which means that it results in a net decrease in performance. Still, it was useful to implement to see that it speeds up these kernel calls.
+
+
+
+Figure 3
+
+
+### Cache first bounce
+Recall that path tracing involves many iterations of shooting rays into the scene and those rays bounce up to a certain number of times. The reason to cache the first bounce is that every iteration, the initial bounce of each ray will be the every time. It is only later bonuces that are non-deterministic, since some materials reflect light randomly. We can save time by caching the first bounce into a separate buffer of global memory during the first iteration and then loading that cache during subsequent iterations.
+
+The following values are based on Image 2. The first iteration's invocation of computeIntersections and subsequent writing to the cache took 295 and 114 microseconds, respectively. Later iterations only took 154 microseconds to load the first bounce from the cache. So based on the image tested, it takes twice as long to compute the intersections as it does to load from the cache. This difference would be even greater if the scene included a more complex mesh. Obviously, the benefits of first-bounce caching become harder and harder to detect as the number of bounces increase because more and more time is spent having to run computeIntersections anyway.
+
+
+### Arbitrary mesh loading
+The initial codebase supported spawning only cubes and spheres. These shapes were generated without loading `.obj` files and without any representation of vertices, faces, triangles, or anything in memory. Instead, the code simply used the geometric properties of cubes and spheres to determine whether a ray headed in a particular direction from a particular location would intersect the object. The goal of this feature was to be able to spawn objects in the scene using `.obj` files.
+
+The [tinyobjloader](https://github.com/tinyobjloader/tinyobjloader) library was used to parse an `.obj` file into a set of `Triangle` structs (each `Triangle` is 60 bytes). The cow mesh has 5,803 triangles and the UFO mesh has 3,854 triangles. These triangles are organized into an octree before being loaded onto the GPU, which is detailed next.
+
+
+Image 5: Meshes loaded
+
+
+### Octree
+
+An [octree](https://en.wikipedia.org/wiki/Octree#:~:text=An%20octree%20is%20a%20tree,three%2Ddimensional%20analog%20of%20quadtrees.) is a space partitioning data structure that makes it faster to determine if a ray intersects a mesh of triangles, and if so, which exact triangle. The tree root represents the entire bounding box around the mesh. The root's eight children represent eight subdivisions of the mesh's bounding box. These subdivisons are recursively subdivided a chosen number of times. Then, each triangle in the mesh is associated with one or more boxes that the triangle intersects.
+
+In order to quickly find the triangle(s) intersected by a particular ray, start at the root and test whether the ray intersects the bounding box. If yes, do the same check on each child bounding box. Repeat this, recursively, until you reach some number of leaf nodes. At that point, you can see whether the ray intersects any of those triangles that the leaf nodes point to. This is a major improvement because the number of triangles searched grows logarithmically instead of linearly.
+
+
+
+Figure 4
+
+
+After generating the set of `Triangle` structs, the goal was to build an octree and then insert each `Triangle`, associating it with one or more leaf nodes.
+
+The octree was represented linearly, as an array, where the eight children of node `i` are found starting at position `8*i+1`. For example, the root is stored at index `0` and its children are at indices `1` through `8`. In addition, an octree of depth `d` has `(8^d-1)/7` nodes, so an octree of depth 1 is just the root which represents the bounding box. Octrees of depth `2`, `3`, and `4` have `9`, `73`, and `585`, nodes respectively.
+
+The process of inserting the `Triangle` structs is similar to the process described for querying for intersections; we recursively ask whether a node contains the triangle. If yes, query each of the child nodes. Repeat until one or more leaf is reached.
+
+I'd like to share one particularly pernicious bug that stumped me for longer than I am willing to admit. While inserting triangles into the tree, you have to have some way of determining if a node _contains_ that triangle. My initial approach was to determine whether the node contained any of the three points representing the triangle. If yes, associate the triangle with the node. Image 7 shows the result of this approach for a tree of depth 5. The problem got worse the deeper I made the tree. I thought it might be a stack or heap size issue because I used recurison in my computeIntersection kernel.
+
+
+
+Image 7: Octree bug
+
+
+It turns out that as the node volumes got smaller and smaller, certain triangles were intersecting nodes _but didn't have any point in the node_, so they weren't getting associated properly. The solution was to _yoink_ and adapt the triangle-cube intersection formula from [here](https://github.com/juj/MathGeoLib/blob/master/src/Geometry/Triangle.cpp#L697). Live and learn.
+
+Image 6 shows the scene used to test various octree depths. Figure 5 shows that there is a significant beneift to adding depth (granularity) to the tree until a depth of three, and then the querying becomes burdensome. I suspect that this result could have to do with thread stack size, since the recursion depth gets deeper the deeper the tree gets. Also, the deeper the tree, the more nodes a single triangle intersects, so there end up actually being a lot of node checks.
+
+
+
+Image 6: Test image for octree
+
+
+
+
+Figure 5
+
+
+### Refraction with Fresnel effect
+Refraction was fun and relatively painless to implement. Using `glm::refract()` made it pretty easy. The most interesting part was realizing how to determine if a ray was intersecting a mesh triangle from the "inside" or "outside". The solution is to compute the angle between the triangle's normal and the ray's direction. If this angle is greater than 90 degrees, then the ray must have hit the outside of the triangle. This handy dandy formula takes care of it:
+```
+float ang = glm::acos(glm::dot(r.direction, normal) / (glm::length(r.direction) * glm::length(normal)));
+bool outside = glm::degrees(ang) > 90;
+```
+
+
+
+Image 7: Refraction with Fresnel effect
+
+
+As for the Fresnel effect, I used the function that I found [here](https://blog.demofox.org/2017/01/09/raytracing-reflection-refraction-fresnel-total-internal-reflection-and-beers-law/) and then did probabilistic thresholding using R.
+
+### Custom Python script for generating scene files
+It became tiresome editing the scene `.txt` files and making sure the various objects pointed to the correct materials, and making sure the numbering on objects and materials was correct. I wrote a Python script, located in `/scenes/scenegenerator.py`, which made it much easier to automatically generate these files and retain sanity.
+
+
+## Concluding thoughts
+
+
+
+me irl
+
+
+## Refrences
+- Ray-triangle intersection function from [here](https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm)
+- Triangle-box intersection function from [here](https://github.com/juj/MathGeoLib/blob/master/src/Geometry/Triangle.cpp#L697)
+- Fresnel effect function from [here](https://blog.demofox.org/2017/01/09/raytracing-reflection-refraction-fresnel-total-internal-reflection-and-beers-law/)
+- How to actually use `thrust` from [here](https://stackoverflow.com/questions/12047961/cuda-thrust-how-to-realize-partition-that-supports-stencil)
\ No newline at end of file
diff --git a/data/fb_data b/data/fb_data
new file mode 100644
index 00000000..2c634ab7
--- /dev/null
+++ b/data/fb_data
@@ -0,0 +1,9 @@
+cache first bounce OFF
+
+
+
+cache first bounce ON
+computeIntersections 295
+cacheWrite 114
+
+cacheRead 154
\ No newline at end of file
diff --git a/data/figures.xlsx b/data/figures.xlsx
new file mode 100644
index 00000000..5162fd43
Binary files /dev/null and b/data/figures.xlsx differ
diff --git a/data/ot_data b/data/ot_data
new file mode 100644
index 00000000..7c3d59f4
--- /dev/null
+++ b/data/ot_data
@@ -0,0 +1,222 @@
+DEPTH = 0
+Allocated triangle memory (348242 bytes)
+Allocated tree memory (248 bytes)
+iteration times
+0 11113
+1 37670
+2 37349
+3 37624
+4 37711
+5 36435
+6 36596
+7 36309
+TOTAL 270810
+computeIntersections times
+0 10904
+1 37466
+2 37157
+3 37430
+4 37545
+5 36282
+6 36449
+7 36163
+TOTAL 269399
+shadeFakeMaterial times
+0 200
+1 190
+2 179
+3 179
+4 154
+5 142
+6 136
+7 135
+TOTAL 1318
+
+
+
+DEPTH = 1
+Allocated triangle memory (370082 bytes)
+Allocated tree memory (2232 bytes)
+
+iteration times
+0 3316
+1 30423
+2 31358
+3 32140
+4 29289
+5 26898
+6 24610
+7 22560
+TOTAL 200597
+computeIntersections times
+0 3099
+1 30235
+2 31164
+3 31974
+4 29129
+5 26753
+6 24471
+7 22423
+TOTAL 199251
+shadeFakeMaterial times
+0 210
+1 183
+2 188
+3 159
+4 155
+5 141
+6 136
+7 131
+TOTAL 1307
+
+
+DEPTH = 2
+Allocated triangle memory (441242 bytes)
+Allocated tree memory (18104 bytes)
+
+iteration times
+0 1517
+1 22031
+2 21591
+3 19786
+4 17585
+5 15514
+6 13855
+7 12545
+TOTAL 124428
+computeIntersections times
+0 1322
+1 21839
+2 21394
+3 19620
+4 17429
+5 15362
+6 13701
+7 12411
+TOTAL 123080
+shadeFakeMaterial times
+0 188
+1 189
+2 190
+3 161
+4 150
+5 149
+6 148
+7 129
+TOTAL 1307
+
+
+DEPTH = 3
+Allocated triangle memory (561182 bytes)
+Allocated tree memory (145080 bytes)
+
+iteration times
+0 1626
+1 18450
+2 17742
+3 14936
+4 13597
+5 11771
+6 10155
+7 9650
+TOTAL 97929
+computeIntersections times
+0 1391
+1 18264
+2 17558
+3 14768
+4 13436
+5 11619
+6 10011
+7 9516
+TOTAL 96566
+shadeFakeMaterial times
+0 229
+1 182
+2 171
+3 160
+4 151
+5 144
+6 136
+7 127
+TOTAL 1303
+
+DEPTH = 4
+Allocated triangle memory (798602 bytes)
+Allocated tree memory (1160888 bytes)
+
+iteration times
+0 3018
+1 38168
+2 35103
+3 29848
+4 25680
+5 21351
+6 18054
+7 15600
+TOTAL 186825
+computeIntersections times
+0 2814
+1 37979
+2 34924
+3 29677
+4 25526
+5 21198
+6 17914
+7 15470
+TOTAL 185505
+shadeFakeMaterial times
+0 198
+1 183
+2 173
+3 167
+4 150
+5 149
+6 136
+7 127
+TOTAL 1287
+
+
+DEPTH = 5
+Allocated triangle memory (1373282 bytes)
+Allocated tree memory (9287352 bytes)
+numPaths
+0 640000
+1 640000
+2 640000
+3 640000
+4 640000
+5 640000
+6 640000
+7 640000
+TOTAL 5120001
+iteration times
+0 9945
+1 97615
+2 86318
+3 70876
+4 58390
+5 48984
+6 40971
+7 34946
+TOTAL 448049
+computeIntersections times
+0 9747
+1 97413
+2 86135
+3 70706
+4 58227
+5 48833
+6 40827
+7 34810
+TOTAL 446701
+shadeFakeMaterial times
+0 189
+1 194
+2 172
+3 162
+4 153
+5 142
+6 135
+7 127
+TOTAL 1278
diff --git a/data/sc_data b/data/sc_data
new file mode 100644
index 00000000..66873829
--- /dev/null
+++ b/data/sc_data
@@ -0,0 +1,126 @@
+depth = 8
+800x800 image
+
+STREAM COMPACTION OFF
+numPaths
+0 640000
+1 640000
+2 640000
+3 640000
+4 640000
+5 640000
+6 640000
+7 640000
+TOTAL 5120001
+iteration times
+0 463
+1 488
+2 494
+3 503
+4 472
+5 439
+6 442
+7 442
+TOTAL 3746
+computeIntersections times
+0 269
+1 299
+2 316
+3 313
+4 304
+5 276
+6 290
+7 295
+TOTAL 2365
+shadeFakeMaterial times
+0 189
+1 187
+2 175
+3 187
+4 166
+5 160
+6 150
+7 145
+TOTAL 1362
+sortMaterials times
+0 0
+1 0
+2 0
+3 0
+4 0
+5 0
+6 0
+7 0
+TOTAL 0
+streamCompaction times
+0 0
+1 0
+2 0
+3 0
+4 0
+5 0
+6 0
+7 0
+TOTAL 0
+
+STREAM COMPACTION ON
+numPaths
+0 640000
+1 523681
+2 362000
+3 289316
+4 240794
+5 202080
+6 170631
+7 144874
+TOTAL 2573378
+iteration times
+0 3432
+1 3033
+2 2207
+3 1798
+4 1656
+5 1435
+6 1376
+7 1278
+TOTAL 16219
+computeIntersections times
+0 298
+1 292
+2 224
+3 207
+4 181
+5 163
+6 148
+7 137
+TOTAL 1654
+shadeFakeMaterial times
+0 231
+1 209
+2 166
+3 129
+4 119
+5 102
+6 103
+7 88
+TOTAL 1151
+sortMaterials times
+0 0
+1 0
+2 0
+3 0
+4 0
+5 0
+6 0
+7 0
+TOTAL 0
+streamCompaction times
+0 2896
+1 2526
+2 1810
+3 1456
+4 1351
+5 1165
+6 1118
+7 1046
+TOTAL 13372
\ No newline at end of file
diff --git a/data/sm_data b/data/sm_data
new file mode 100644
index 00000000..f08301cf
--- /dev/null
+++ b/data/sm_data
@@ -0,0 +1,75 @@
+
+SM OFF
+iteration times
+0 585
+1 652
+2 603
+3 507
+4 437
+5 437
+6 426
+7 428
+TOTAL 4078
+computeIntersections times
+0 334
+1 394
+2 387
+3 326
+4 286
+5 281
+6 282
+7 284
+TOTAL 2577
+shadeFakeMaterial times
+0 245
+1 255
+2 213
+3 179
+4 149
+5 153
+6 142
+7 141
+TOTAL 1479
+
+
+SM ON
+iteration times
+0 7310
+1 6663
+2 6976
+3 6445
+4 6222
+5 6387
+6 6612
+7 6427
+TOTAL 53044
+computeIntersections times
+0 282
+1 268
+2 253
+3 204
+4 189
+5 174
+6 164
+7 158
+TOTAL 1695
+shadeFakeMaterial times
+0 210
+1 175
+2 154
+3 138
+4 131
+5 117
+6 109
+7 104
+TOTAL 1142
+sortMaterials times
+0 6812
+1 6213
+2 6564
+3 6097
+4 5895
+5 6091
+6 6333
+7 6159
+TOTAL 50167
diff --git a/external/include/common.h b/external/include/common.h
new file mode 100644
index 00000000..d2c1fed9
--- /dev/null
+++ b/external/include/common.h
@@ -0,0 +1,132 @@
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+#define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__)
+
+/**
+ * Check for CUDA errors; print and exit if there was a problem.
+ */
+void checkCUDAErrorFn(const char *msg, const char *file = NULL, int line = -1);
+
+inline int ilog2(int x) {
+ int lg = 0;
+ while (x >>= 1) {
+ ++lg;
+ }
+ return lg;
+}
+
+inline int ilog2ceil(int x) {
+ return x == 1 ? 0 : ilog2(x - 1) + 1;
+}
+
+namespace StreamCompaction {
+ namespace Common {
+ __global__ void kernMapToBoolean(int n, int *bools, const int *idata);
+
+ __global__ void kernScatter(int n, int *odata,
+ const int *idata, const int *bools, const int *indices);
+
+ /**
+ * This class is used for timing the performance
+ * Uncopyable and unmovable
+ *
+ * Adapted from WindyDarian(https://github.com/WindyDarian)
+ */
+ class PerformanceTimer
+ {
+ public:
+ PerformanceTimer()
+ {
+ cudaEventCreate(&event_start);
+ cudaEventCreate(&event_end);
+ }
+
+ ~PerformanceTimer()
+ {
+ cudaEventDestroy(event_start);
+ cudaEventDestroy(event_end);
+ }
+
+ void startCpuTimer()
+ {
+ if (cpu_timer_started) { throw std::runtime_error("CPU timer already started"); }
+ cpu_timer_started = true;
+
+ time_start_cpu = std::chrono::high_resolution_clock::now();
+ }
+
+ void endCpuTimer()
+ {
+ time_end_cpu = std::chrono::high_resolution_clock::now();
+
+ if (!cpu_timer_started) { throw std::runtime_error("CPU timer not started"); }
+
+ std::chrono::duration duro = time_end_cpu - time_start_cpu;
+ prev_elapsed_time_cpu_milliseconds =
+ static_cast(duro.count());
+
+ cpu_timer_started = false;
+ }
+
+ void startGpuTimer()
+ {
+ if (gpu_timer_started) { throw std::runtime_error("GPU timer already started"); }
+ gpu_timer_started = true;
+
+ cudaEventRecord(event_start);
+ }
+
+ void endGpuTimer()
+ {
+ cudaEventRecord(event_end);
+ cudaEventSynchronize(event_end);
+
+ if (!gpu_timer_started) { throw std::runtime_error("GPU timer not started"); }
+
+ cudaEventElapsedTime(&prev_elapsed_time_gpu_milliseconds, event_start, event_end);
+ gpu_timer_started = false;
+ }
+
+ float getCpuElapsedTimeForPreviousOperation() //noexcept //(damn I need VS 2015
+ {
+ return prev_elapsed_time_cpu_milliseconds;
+ }
+
+ float getGpuElapsedTimeForPreviousOperation() //noexcept
+ {
+ return prev_elapsed_time_gpu_milliseconds;
+ }
+
+ // remove copy and move functions
+ PerformanceTimer(const PerformanceTimer&) = delete;
+ PerformanceTimer(PerformanceTimer&&) = delete;
+ PerformanceTimer& operator=(const PerformanceTimer&) = delete;
+ PerformanceTimer& operator=(PerformanceTimer&&) = delete;
+
+ private:
+ cudaEvent_t event_start = nullptr;
+ cudaEvent_t event_end = nullptr;
+
+ using time_point_t = std::chrono::high_resolution_clock::time_point;
+ time_point_t time_start_cpu;
+ time_point_t time_end_cpu;
+
+ bool cpu_timer_started = false;
+ bool gpu_timer_started = false;
+
+ float prev_elapsed_time_cpu_milliseconds = 0.f;
+ float prev_elapsed_time_gpu_milliseconds = 0.f;
+ };
+ }
+}
diff --git a/external/include/earcut.hpp b/external/include/earcut.hpp
new file mode 100644
index 00000000..64c6d3d8
--- /dev/null
+++ b/external/include/earcut.hpp
@@ -0,0 +1,820 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+namespace mapbox {
+
+namespace util {
+
+template struct nth {
+ inline static typename std::tuple_element::type
+ get(const T& t) { return std::get(t); };
+};
+
+}
+
+namespace detail {
+
+template
+class Earcut {
+public:
+ std::vector indices;
+ std::size_t vertices = 0;
+
+ template
+ void operator()(const Polygon& points);
+
+private:
+ struct Node {
+ Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {}
+ Node(const Node&) = delete;
+ Node& operator=(const Node&) = delete;
+ Node(Node&&) = delete;
+ Node& operator=(Node&&) = delete;
+
+ const N i;
+ const double x;
+ const double y;
+
+ // previous and next vertice nodes in a polygon ring
+ Node* prev = nullptr;
+ Node* next = nullptr;
+
+ // z-order curve value
+ int32_t z = 0;
+
+ // previous and next nodes in z-order
+ Node* prevZ = nullptr;
+ Node* nextZ = nullptr;
+
+ // indicates whether this is a steiner point
+ bool steiner = false;
+ };
+
+ template Node* linkedList(const Ring& points, const bool clockwise);
+ Node* filterPoints(Node* start, Node* end = nullptr);
+ void earcutLinked(Node* ear, int pass = 0);
+ bool isEar(Node* ear);
+ bool isEarHashed(Node* ear);
+ Node* cureLocalIntersections(Node* start);
+ void splitEarcut(Node* start);
+ template Node* eliminateHoles(const Polygon& points, Node* outerNode);
+ Node* eliminateHole(Node* hole, Node* outerNode);
+ Node* findHoleBridge(Node* hole, Node* outerNode);
+ bool sectorContainsSector(const Node* m, const Node* p);
+ void indexCurve(Node* start);
+ Node* sortLinked(Node* list);
+ int32_t zOrder(const double x_, const double y_);
+ Node* getLeftmost(Node* start);
+ bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
+ bool isValidDiagonal(Node* a, Node* b);
+ double area(const Node* p, const Node* q, const Node* r) const;
+ bool equals(const Node* p1, const Node* p2);
+ bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
+ bool onSegment(const Node* p, const Node* q, const Node* r);
+ int sign(double val);
+ bool intersectsPolygon(const Node* a, const Node* b);
+ bool locallyInside(const Node* a, const Node* b);
+ bool middleInside(const Node* a, const Node* b);
+ Node* splitPolygon(Node* a, Node* b);
+ template Node* insertNode(std::size_t i, const Point& p, Node* last);
+ void removeNode(Node* p);
+
+ bool hashing;
+ double minX, maxX;
+ double minY, maxY;
+ double inv_size = 0;
+
+ template >
+ class ObjectPool {
+ public:
+ ObjectPool() { }
+ ObjectPool(std::size_t blockSize_) {
+ reset(blockSize_);
+ }
+ ~ObjectPool() {
+ clear();
+ }
+ template
+ T* construct(Args&&... args) {
+ if (currentIndex >= blockSize) {
+ currentBlock = alloc_traits::allocate(alloc, blockSize);
+ allocations.emplace_back(currentBlock);
+ currentIndex = 0;
+ }
+ T* object = ¤tBlock[currentIndex++];
+ alloc_traits::construct(alloc, object, std::forward(args)...);
+ return object;
+ }
+ void reset(std::size_t newBlockSize) {
+ for (auto allocation : allocations) {
+ alloc_traits::deallocate(alloc, allocation, blockSize);
+ }
+ allocations.clear();
+ blockSize = std::max(1, newBlockSize);
+ currentBlock = nullptr;
+ currentIndex = blockSize;
+ }
+ void clear() { reset(blockSize); }
+ private:
+ T* currentBlock = nullptr;
+ std::size_t currentIndex = 1;
+ std::size_t blockSize = 1;
+ std::vector allocations;
+ Alloc alloc;
+ typedef typename std::allocator_traits alloc_traits;
+ };
+ ObjectPool nodes;
+};
+
+template template
+void Earcut::operator()(const Polygon& points) {
+ // reset
+ indices.clear();
+ vertices = 0;
+
+ if (points.empty()) return;
+
+ double x;
+ double y;
+ int threshold = 80;
+ std::size_t len = 0;
+
+ for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
+ threshold -= static_cast(points[i].size());
+ len += points[i].size();
+ }
+
+ //estimate size of nodes and indices
+ nodes.reset(len * 3 / 2);
+ indices.reserve(len + points[0].size());
+
+ Node* outerNode = linkedList(points[0], true);
+ if (!outerNode || outerNode->prev == outerNode->next) return;
+
+ if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
+
+ // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
+ hashing = threshold < 0;
+ if (hashing) {
+ Node* p = outerNode->next;
+ minX = maxX = outerNode->x;
+ minY = maxY = outerNode->y;
+ do {
+ x = p->x;
+ y = p->y;
+ minX = std::min(minX, x);
+ minY = std::min(minY, y);
+ maxX = std::max(maxX, x);
+ maxY = std::max(maxY, y);
+ p = p->next;
+ } while (p != outerNode);
+
+ // minX, minY and size are later used to transform coords into integers for z-order calculation
+ inv_size = std::max(maxX - minX, maxY - minY);
+ inv_size = inv_size != .0 ? (1. / inv_size) : .0;
+ }
+
+ earcutLinked(outerNode);
+
+ nodes.clear();
+}
+
+// create a circular doubly linked list from polygon points in the specified winding order
+template template
+typename Earcut::Node*
+Earcut::linkedList(const Ring& points, const bool clockwise) {
+ using Point = typename Ring::value_type;
+ double sum = 0;
+ const std::size_t len = points.size();
+ std::size_t i, j;
+ Node* last = nullptr;
+
+ // calculate original winding order of a polygon ring
+ for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
+ const auto& p1 = points[i];
+ const auto& p2 = points[j];
+ const double p20 = util::nth<0, Point>::get(p2);
+ const double p10 = util::nth<0, Point>::get(p1);
+ const double p11 = util::nth<1, Point>::get(p1);
+ const double p21 = util::nth<1, Point>::get(p2);
+ sum += (p20 - p10) * (p11 + p21);
+ }
+
+ // link points into circular doubly-linked list in the specified winding order
+ if (clockwise == (sum > 0)) {
+ for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
+ } else {
+ for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
+ }
+
+ if (last && equals(last, last->next)) {
+ removeNode(last);
+ last = last->next;
+ }
+
+ vertices += len;
+
+ return last;
+}
+
+// eliminate colinear or duplicate points
+template
+typename Earcut::Node*
+Earcut::filterPoints(Node* start, Node* end) {
+ if (!end) end = start;
+
+ Node* p = start;
+ bool again;
+ do {
+ again = false;
+
+ if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
+ removeNode(p);
+ p = end = p->prev;
+
+ if (p == p->next) break;
+ again = true;
+
+ } else {
+ p = p->next;
+ }
+ } while (again || p != end);
+
+ return end;
+}
+
+// main ear slicing loop which triangulates a polygon (given as a linked list)
+template
+void Earcut::earcutLinked(Node* ear, int pass) {
+ if (!ear) return;
+
+ // interlink polygon nodes in z-order
+ if (!pass && hashing) indexCurve(ear);
+
+ Node* stop = ear;
+ Node* prev;
+ Node* next;
+
+ int iterations = 0;
+
+ // iterate through ears, slicing them one by one
+ while (ear->prev != ear->next) {
+ iterations++;
+ prev = ear->prev;
+ next = ear->next;
+
+ if (hashing ? isEarHashed(ear) : isEar(ear)) {
+ // cut off the triangle
+ indices.emplace_back(prev->i);
+ indices.emplace_back(ear->i);
+ indices.emplace_back(next->i);
+
+ removeNode(ear);
+
+ // skipping the next vertice leads to less sliver triangles
+ ear = next->next;
+ stop = next->next;
+
+ continue;
+ }
+
+ ear = next;
+
+ // if we looped through the whole remaining polygon and can't find any more ears
+ if (ear == stop) {
+ // try filtering points and slicing again
+ if (!pass) earcutLinked(filterPoints(ear), 1);
+
+ // if this didn't work, try curing all small self-intersections locally
+ else if (pass == 1) {
+ ear = cureLocalIntersections(filterPoints(ear));
+ earcutLinked(ear, 2);
+
+ // as a last resort, try splitting the remaining polygon into two
+ } else if (pass == 2) splitEarcut(ear);
+
+ break;
+ }
+ }
+}
+
+// check whether a polygon node forms a valid ear with adjacent nodes
+template
+bool Earcut::isEar(Node* ear) {
+ const Node* a = ear->prev;
+ const Node* b = ear;
+ const Node* c = ear->next;
+
+ if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
+
+ // now make sure we don't have other points inside the potential ear
+ Node* p = ear->next->next;
+
+ while (p != ear->prev) {
+ if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+ area(p->prev, p, p->next) >= 0) return false;
+ p = p->next;
+ }
+
+ return true;
+}
+
+template
+bool Earcut::isEarHashed(Node* ear) {
+ const Node* a = ear->prev;
+ const Node* b = ear;
+ const Node* c = ear->next;
+
+ if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
+
+ // triangle bbox; min & max are calculated like this for speed
+ const double minTX = std::min(a->x, std::min(b->x, c->x));
+ const double minTY = std::min(a->y, std::min(b->y, c->y));
+ const double maxTX = std::max(a->x, std::max(b->x, c->x));
+ const double maxTY = std::max(a->y, std::max(b->y, c->y));
+
+ // z-order range for the current triangle bbox;
+ const int32_t minZ = zOrder(minTX, minTY);
+ const int32_t maxZ = zOrder(maxTX, maxTY);
+
+ // first look for points inside the triangle in increasing z-order
+ Node* p = ear->nextZ;
+
+ while (p && p->z <= maxZ) {
+ if (p != ear->prev && p != ear->next &&
+ pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+ area(p->prev, p, p->next) >= 0) return false;
+ p = p->nextZ;
+ }
+
+ // then look for points in decreasing z-order
+ p = ear->prevZ;
+
+ while (p && p->z >= minZ) {
+ if (p != ear->prev && p != ear->next &&
+ pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) &&
+ area(p->prev, p, p->next) >= 0) return false;
+ p = p->prevZ;
+ }
+
+ return true;
+}
+
+// go through all polygon nodes and cure small local self-intersections
+template
+typename Earcut::Node*
+Earcut::cureLocalIntersections(Node* start) {
+ Node* p = start;
+ do {
+ Node* a = p->prev;
+ Node* b = p->next->next;
+
+ // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
+ if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
+ indices.emplace_back(a->i);
+ indices.emplace_back(p->i);
+ indices.emplace_back(b->i);
+
+ // remove two nodes involved
+ removeNode(p);
+ removeNode(p->next);
+
+ p = start = b;
+ }
+ p = p->next;
+ } while (p != start);
+
+ return filterPoints(p);
+}
+
+// try splitting polygon into two and triangulate them independently
+template
+void Earcut::splitEarcut(Node* start) {
+ // look for a valid diagonal that divides the polygon into two
+ Node* a = start;
+ do {
+ Node* b = a->next->next;
+ while (b != a->prev) {
+ if (a->i != b->i && isValidDiagonal(a, b)) {
+ // split the polygon in two by the diagonal
+ Node* c = splitPolygon(a, b);
+
+ // filter colinear points around the cuts
+ a = filterPoints(a, a->next);
+ c = filterPoints(c, c->next);
+
+ // run earcut on each half
+ earcutLinked(a);
+ earcutLinked(c);
+ return;
+ }
+ b = b->next;
+ }
+ a = a->next;
+ } while (a != start);
+}
+
+// link every hole into the outer loop, producing a single-ring polygon without holes
+template template
+typename Earcut::Node*
+Earcut::eliminateHoles(const Polygon& points, Node* outerNode) {
+ const size_t len = points.size();
+
+ std::vector queue;
+ for (size_t i = 1; i < len; i++) {
+ Node* list = linkedList(points[i], false);
+ if (list) {
+ if (list == list->next) list->steiner = true;
+ queue.push_back(getLeftmost(list));
+ }
+ }
+ std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) {
+ return a->x < b->x;
+ });
+
+ // process holes from left to right
+ for (size_t i = 0; i < queue.size(); i++) {
+ outerNode = eliminateHole(queue[i], outerNode);
+ outerNode = filterPoints(outerNode, outerNode->next);
+ }
+
+ return outerNode;
+}
+
+// find a bridge between vertices that connects hole with an outer ring and and link it
+template
+typename Earcut::Node*
+Earcut::eliminateHole(Node* hole, Node* outerNode) {
+ Node* bridge = findHoleBridge(hole, outerNode);
+ if (!bridge) {
+ return outerNode;
+ }
+
+ Node* bridgeReverse = splitPolygon(bridge, hole);
+
+ // filter collinear points around the cuts
+ Node* filteredBridge = filterPoints(bridge, bridge->next);
+ filterPoints(bridgeReverse, bridgeReverse->next);
+
+ // Check if input node was removed by the filtering
+ return outerNode == bridge ? filteredBridge : outerNode;
+}
+
+// David Eberly's algorithm for finding a bridge between hole and outer polygon
+template
+typename Earcut::Node*
+Earcut::findHoleBridge(Node* hole, Node* outerNode) {
+ Node* p = outerNode;
+ double hx = hole->x;
+ double hy = hole->y;
+ double qx = -std::numeric_limits::infinity();
+ Node* m = nullptr;
+
+ // find a segment intersected by a ray from the hole's leftmost Vertex to the left;
+ // segment's endpoint with lesser x will be potential connection Vertex
+ do {
+ if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
+ double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
+ if (x <= hx && x > qx) {
+ qx = x;
+ if (x == hx) {
+ if (hy == p->y) return p;
+ if (hy == p->next->y) return p->next;
+ }
+ m = p->x < p->next->x ? p : p->next;
+ }
+ }
+ p = p->next;
+ } while (p != outerNode);
+
+ if (!m) return 0;
+
+ if (hx == qx) return m; // hole touches outer segment; pick leftmost endpoint
+
+ // look for points inside the triangle of hole Vertex, segment intersection and endpoint;
+ // if there are no points found, we have a valid connection;
+ // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
+
+ const Node* stop = m;
+ double tanMin = std::numeric_limits::infinity();
+ double tanCur = 0;
+
+ p = m;
+ double mx = m->x;
+ double my = m->y;
+
+ do {
+ if (hx >= p->x && p->x >= mx && hx != p->x &&
+ pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
+
+ tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
+
+ if (locallyInside(p, hole) &&
+ (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) {
+ m = p;
+ tanMin = tanCur;
+ }
+ }
+
+ p = p->next;
+ } while (p != stop);
+
+ return m;
+}
+
+// whether sector in vertex m contains sector in vertex p in the same coordinates
+template
+bool Earcut::sectorContainsSector(const Node* m, const Node* p) {
+ return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0;
+}
+
+// interlink polygon nodes in z-order
+template
+void Earcut::indexCurve(Node* start) {
+ assert(start);
+ Node* p = start;
+
+ do {
+ p->z = p->z ? p->z : zOrder(p->x, p->y);
+ p->prevZ = p->prev;
+ p->nextZ = p->next;
+ p = p->next;
+ } while (p != start);
+
+ p->prevZ->nextZ = nullptr;
+ p->prevZ = nullptr;
+
+ sortLinked(p);
+}
+
+// Simon Tatham's linked list merge sort algorithm
+// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
+template
+typename Earcut::Node*
+Earcut::sortLinked(Node* list) {
+ assert(list);
+ Node* p;
+ Node* q;
+ Node* e;
+ Node* tail;
+ int i, numMerges, pSize, qSize;
+ int inSize = 1;
+
+ for (;;) {
+ p = list;
+ list = nullptr;
+ tail = nullptr;
+ numMerges = 0;
+
+ while (p) {
+ numMerges++;
+ q = p;
+ pSize = 0;
+ for (i = 0; i < inSize; i++) {
+ pSize++;
+ q = q->nextZ;
+ if (!q) break;
+ }
+
+ qSize = inSize;
+
+ while (pSize > 0 || (qSize > 0 && q)) {
+
+ if (pSize == 0) {
+ e = q;
+ q = q->nextZ;
+ qSize--;
+ } else if (qSize == 0 || !q) {
+ e = p;
+ p = p->nextZ;
+ pSize--;
+ } else if (p->z <= q->z) {
+ e = p;
+ p = p->nextZ;
+ pSize--;
+ } else {
+ e = q;
+ q = q->nextZ;
+ qSize--;
+ }
+
+ if (tail) tail->nextZ = e;
+ else list = e;
+
+ e->prevZ = tail;
+ tail = e;
+ }
+
+ p = q;
+ }
+
+ tail->nextZ = nullptr;
+
+ if (numMerges <= 1) return list;
+
+ inSize *= 2;
+ }
+}
+
+// z-order of a Vertex given coords and size of the data bounding box
+template
+int32_t Earcut::zOrder(const double x_, const double y_) {
+ // coords are transformed into non-negative 15-bit integer range
+ int32_t x = static_cast(32767.0 * (x_ - minX) * inv_size);
+ int32_t y = static_cast(32767.0 * (y_ - minY) * inv_size);
+
+ x = (x | (x << 8)) & 0x00FF00FF;
+ x = (x | (x << 4)) & 0x0F0F0F0F;
+ x = (x | (x << 2)) & 0x33333333;
+ x = (x | (x << 1)) & 0x55555555;
+
+ y = (y | (y << 8)) & 0x00FF00FF;
+ y = (y | (y << 4)) & 0x0F0F0F0F;
+ y = (y | (y << 2)) & 0x33333333;
+ y = (y | (y << 1)) & 0x55555555;
+
+ return x | (y << 1);
+}
+
+// find the leftmost node of a polygon ring
+template
+typename Earcut::Node*
+Earcut::getLeftmost(Node* start) {
+ Node* p = start;
+ Node* leftmost = start;
+ do {
+ if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y))
+ leftmost = p;
+ p = p->next;
+ } while (p != start);
+
+ return leftmost;
+}
+
+// check if a point lies within a convex triangle
+template
+bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
+ return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
+ (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
+ (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
+}
+
+// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
+template
+bool Earcut::isValidDiagonal(Node* a, Node* b) {
+ return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges
+ ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
+ (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors
+ (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case
+}
+
+// signed area of a triangle
+template
+double Earcut::area(const Node* p, const Node* q, const Node* r) const {
+ return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
+}
+
+// check if two points are equal
+template
+bool Earcut::equals(const Node* p1, const Node* p2) {
+ return p1->x == p2->x && p1->y == p2->y;
+}
+
+// check if two segments intersect
+template
+bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
+ int o1 = sign(area(p1, q1, p2));
+ int o2 = sign(area(p1, q1, q2));
+ int o3 = sign(area(p2, q2, p1));
+ int o4 = sign(area(p2, q2, q1));
+
+ if (o1 != o2 && o3 != o4) return true; // general case
+
+ if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
+ if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
+ if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
+ if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
+
+ return false;
+}
+
+// for collinear points p, q, r, check if point q lies on segment pr
+template
+bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) {
+ return q->x <= std::max(p->x, r->x) &&
+ q->x >= std::min(p->x, r->x) &&
+ q->y <= std::max(p->y, r->y) &&
+ q->y >= std::min(p->y, r->y);
+}
+
+template
+int Earcut::sign(double val) {
+ return (0.0 < val) - (val < 0.0);
+}
+
+// check if a polygon diagonal intersects any polygon segments
+template
+bool Earcut::intersectsPolygon(const Node* a, const Node* b) {
+ const Node* p = a;
+ do {
+ if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i &&
+ intersects(p, p->next, a, b)) return true;
+ p = p->next;
+ } while (p != a);
+
+ return false;
+}
+
+// check if a polygon diagonal is locally inside the polygon
+template
+bool Earcut::locallyInside(const Node* a, const Node* b) {
+ return area(a->prev, a, a->next) < 0 ?
+ area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 :
+ area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
+}
+
+// check if the middle Vertex of a polygon diagonal is inside the polygon
+template
+bool Earcut::middleInside(const Node* a, const Node* b) {
+ const Node* p = a;
+ bool inside = false;
+ double px = (a->x + b->x) / 2;
+ double py = (a->y + b->y) / 2;
+ do {
+ if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
+ (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
+ inside = !inside;
+ p = p->next;
+ } while (p != a);
+
+ return inside;
+}
+
+// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
+// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
+// single ring
+template
+typename Earcut::Node*
+Earcut::splitPolygon(Node* a, Node* b) {
+ Node* a2 = nodes.construct(a->i, a->x, a->y);
+ Node* b2 = nodes.construct(b->i, b->x, b->y);
+ Node* an = a->next;
+ Node* bp = b->prev;
+
+ a->next = b;
+ b->prev = a;
+
+ a2->next = an;
+ an->prev = a2;
+
+ b2->next = a2;
+ a2->prev = b2;
+
+ bp->next = b2;
+ b2->prev = bp;
+
+ return b2;
+}
+
+// create a node and util::optionally link it with previous one (in a circular doubly linked list)
+template template
+typename Earcut::Node*
+Earcut::insertNode(std::size_t i, const Point& pt, Node* last) {
+ Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
+
+ if (!last) {
+ p->prev = p;
+ p->next = p;
+
+ } else {
+ assert(last);
+ p->next = last->next;
+ p->prev = last;
+ last->next->prev = p;
+ last->next = p;
+ }
+ return p;
+}
+
+template
+void Earcut::removeNode(Node* p) {
+ p->next->prev = p->prev;
+ p->prev->next = p->next;
+
+ if (p->prevZ) p->prevZ->nextZ = p->nextZ;
+ if (p->nextZ) p->nextZ->prevZ = p->prevZ;
+}
+}
+
+template
+std::vector earcut(const Polygon& poly) {
+ mapbox::detail::Earcut earcut;
+ earcut(poly);
+ return std::move(earcut.indices);
+}
+}
\ No newline at end of file
diff --git a/external/include/tiny_obj_loader.h b/external/include/tiny_obj_loader.h
new file mode 100644
index 00000000..707f67b8
--- /dev/null
+++ b/external/include/tiny_obj_loader.h
@@ -0,0 +1,3366 @@
+/*
+The MIT License (MIT)
+Copyright (c) 2012-Present, Syoyo Fujita and many contributors.
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+//
+// version 2.0.0 : Add new object oriented API. 1.x API is still provided.
+// * Support line primitive.
+// * Support points primitive.
+// * Support multiple search path for .mtl(v1 API).
+// * Support vertex weight `vw`(as an tinyobj extension)
+// * Support escaped whitespece in mtllib
+// * Add robust triangulation using Mapbox earcut(TINYOBJLOADER_USE_MAPBOX_EARCUT).
+// version 1.4.0 : Modifed ParseTextureNameAndOption API
+// version 1.3.1 : Make ParseTextureNameAndOption API public
+// version 1.3.0 : Separate warning and error message(breaking API of LoadObj)
+// version 1.2.3 : Added color space extension('-colorspace') to tex opts.
+// version 1.2.2 : Parse multiple group names.
+// version 1.2.1 : Added initial support for line('l') primitive(PR #178)
+// version 1.2.0 : Hardened implementation(#175)
+// version 1.1.1 : Support smoothing groups(#162)
+// version 1.1.0 : Support parsing vertex color(#144)
+// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
+// version 1.0.7 : Support multiple tex options(#126)
+// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124)
+// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43)
+// version 1.0.4 : Support multiple filenames for 'mtllib'(#112)
+// version 1.0.3 : Support parsing texture options(#85)
+// version 1.0.2 : Improve parsing speed by about a factor of 2 for large
+// files(#105)
+// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104)
+// version 1.0.0 : Change data structure. Change license from BSD to MIT.
+//
+
+//
+// Use this in *one* .cc
+// #define TINYOBJLOADER_IMPLEMENTATION
+// #include "tiny_obj_loader.h"
+//
+
+#ifndef TINY_OBJ_LOADER_H_
+#define TINY_OBJ_LOADER_H_
+
+#include