stripifier.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
  2. #include "meshoptimizer.h"
  3. #include <assert.h>
  4. #include <limits.h>
  5. #include <string.h>
  6. // This work is based on:
  7. // Francine Evans, Steven Skiena and Amitabh Varshney. Optimizing Triangle Strips for Fast Rendering. 1996
  8. namespace meshopt
  9. {
  10. static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
  11. {
  12. unsigned int index = 0;
  13. unsigned int iv = ~0u;
  14. for (size_t i = 0; i < buffer_size; ++i)
  15. {
  16. unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
  17. unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc;
  18. if (v < iv)
  19. {
  20. index = unsigned(i);
  21. iv = v;
  22. }
  23. }
  24. return index;
  25. }
  26. static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
  27. {
  28. for (size_t i = 0; i < buffer_size; ++i)
  29. {
  30. unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
  31. if (e0 == a && e1 == b)
  32. return (int(i) << 2) | 2;
  33. else if (e0 == b && e1 == c)
  34. return (int(i) << 2) | 0;
  35. else if (e0 == c && e1 == a)
  36. return (int(i) << 2) | 1;
  37. }
  38. return -1;
  39. }
  40. } // namespace meshopt
  41. size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index)
  42. {
  43. assert(destination != indices);
  44. assert(index_count % 3 == 0);
  45. using namespace meshopt;
  46. meshopt_Allocator allocator;
  47. const size_t buffer_capacity = 8;
  48. unsigned int buffer[buffer_capacity][3] = {};
  49. unsigned int buffer_size = 0;
  50. size_t index_offset = 0;
  51. unsigned int strip[2] = {};
  52. unsigned int parity = 0;
  53. size_t strip_size = 0;
  54. // compute vertex valence; this is used to prioritize starting triangle for strips
  55. unsigned int* valence = allocator.allocate<unsigned int>(vertex_count);
  56. memset(valence, 0, vertex_count * sizeof(unsigned int));
  57. for (size_t i = 0; i < index_count; ++i)
  58. {
  59. unsigned int index = indices[i];
  60. assert(index < vertex_count);
  61. valence[index]++;
  62. }
  63. int next = -1;
  64. while (buffer_size > 0 || index_offset < index_count)
  65. {
  66. assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3));
  67. // fill triangle buffer
  68. while (buffer_size < buffer_capacity && index_offset < index_count)
  69. {
  70. buffer[buffer_size][0] = indices[index_offset + 0];
  71. buffer[buffer_size][1] = indices[index_offset + 1];
  72. buffer[buffer_size][2] = indices[index_offset + 2];
  73. buffer_size++;
  74. index_offset += 3;
  75. }
  76. assert(buffer_size > 0);
  77. if (next >= 0)
  78. {
  79. unsigned int i = next >> 2;
  80. unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
  81. unsigned int v = buffer[i][next & 3];
  82. // ordered removal from the buffer
  83. memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));
  84. buffer_size--;
  85. // update vertex valences for strip start heuristic
  86. valence[a]--;
  87. valence[b]--;
  88. valence[c]--;
  89. // find next triangle (note that edge order flips on every iteration)
  90. // in some cases we need to perform a swap to pick a different outgoing triangle edge
  91. // for [a b c], the default strip edge is [b c], but we might want to use [a c]
  92. int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]);
  93. int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1;
  94. if (cont < 0 && swap >= 0)
  95. {
  96. // [a b c] => [a b a c]
  97. destination[strip_size++] = strip[0];
  98. destination[strip_size++] = v;
  99. // next strip has same winding
  100. // ? a b => b a v
  101. strip[1] = v;
  102. next = swap;
  103. }
  104. else
  105. {
  106. // emit the next vertex in the strip
  107. destination[strip_size++] = v;
  108. // next strip has flipped winding
  109. strip[0] = strip[1];
  110. strip[1] = v;
  111. parity ^= 1;
  112. next = cont;
  113. }
  114. }
  115. else
  116. {
  117. // if we didn't find anything, we need to find the next new triangle
  118. // we use a heuristic to maximize the strip length
  119. unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
  120. unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
  121. // ordered removal from the buffer
  122. memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));
  123. buffer_size--;
  124. // update vertex valences for strip start heuristic
  125. valence[a]--;
  126. valence[b]--;
  127. valence[c]--;
  128. // we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
  129. int ea = findStripNext(buffer, buffer_size, c, b);
  130. int eb = findStripNext(buffer, buffer_size, a, c);
  131. int ec = findStripNext(buffer, buffer_size, b, a);
  132. // in some cases we can have several matching edges; since we can pick any edge, we pick the one with the smallest
  133. // triangle index in the buffer. this reduces the effect of stripification on ACMR and additionally - for unclear
  134. // reasons - slightly improves the stripification efficiency
  135. int mine = INT_MAX;
  136. mine = (ea >= 0 && mine > ea) ? ea : mine;
  137. mine = (eb >= 0 && mine > eb) ? eb : mine;
  138. mine = (ec >= 0 && mine > ec) ? ec : mine;
  139. if (ea == mine)
  140. {
  141. // keep abc
  142. next = ea;
  143. }
  144. else if (eb == mine)
  145. {
  146. // abc -> bca
  147. unsigned int t = a;
  148. a = b, b = c, c = t;
  149. next = eb;
  150. }
  151. else if (ec == mine)
  152. {
  153. // abc -> cab
  154. unsigned int t = c;
  155. c = b, b = a, a = t;
  156. next = ec;
  157. }
  158. if (restart_index)
  159. {
  160. if (strip_size)
  161. destination[strip_size++] = restart_index;
  162. destination[strip_size++] = a;
  163. destination[strip_size++] = b;
  164. destination[strip_size++] = c;
  165. // new strip always starts with the same edge winding
  166. strip[0] = b;
  167. strip[1] = c;
  168. parity = 1;
  169. }
  170. else
  171. {
  172. if (strip_size)
  173. {
  174. // connect last strip using degenerate triangles
  175. destination[strip_size++] = strip[1];
  176. destination[strip_size++] = a;
  177. }
  178. // note that we may need to flip the emitted triangle based on parity
  179. // we always end up with outgoing edge "cb" in the end
  180. unsigned int e0 = parity ? c : b;
  181. unsigned int e1 = parity ? b : c;
  182. destination[strip_size++] = a;
  183. destination[strip_size++] = e0;
  184. destination[strip_size++] = e1;
  185. strip[0] = e0;
  186. strip[1] = e1;
  187. parity ^= 1;
  188. }
  189. }
  190. }
  191. return strip_size;
  192. }
  193. size_t meshopt_stripifyBound(size_t index_count)
  194. {
  195. assert(index_count % 3 == 0);
  196. // worst case without restarts is 2 degenerate indices and 3 indices per triangle
  197. // worst case with restarts is 1 restart index and 3 indices per triangle
  198. return (index_count / 3) * 5;
  199. }
  200. size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index)
  201. {
  202. assert(destination != indices);
  203. size_t offset = 0;
  204. size_t start = 0;
  205. for (size_t i = 0; i < index_count; ++i)
  206. {
  207. if (restart_index && indices[i] == restart_index)
  208. {
  209. start = i + 1;
  210. }
  211. else if (i - start >= 2)
  212. {
  213. unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];
  214. // flip winding for odd triangles
  215. if ((i - start) & 1)
  216. {
  217. unsigned int t = a;
  218. a = b, b = t;
  219. }
  220. // although we use restart indices, strip swaps still produce degenerate triangles, so skip them
  221. if (a != b && a != c && b != c)
  222. {
  223. destination[offset + 0] = a;
  224. destination[offset + 1] = b;
  225. destination[offset + 2] = c;
  226. offset += 3;
  227. }
  228. }
  229. }
  230. return offset;
  231. }
  232. size_t meshopt_unstripifyBound(size_t index_count)
  233. {
  234. assert(index_count == 0 || index_count >= 3);
  235. return (index_count == 0) ? 0 : (index_count - 2) * 3;
  236. }