par_octasphere.h 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. // OCTASPHERE :: https://prideout.net/blog/octasphere
  2. // Tiny malloc-free library that generates triangle meshes for spheres, rounded boxes, and capsules.
  3. //
  4. // This library proffers the following functions:
  5. //
  6. // - par_octasphere_get_counts
  7. // - par_octasphere_populate
  8. //
  9. // Usage example:
  10. //
  11. // /* Specify a 100x100x20 rounded box. */
  12. // const par_octasphere_config cfg = {
  13. // .corner_radius = 5,
  14. // .width = 100,
  15. // .height = 100,
  16. // .depth = 20,
  17. // .num_subdivisions = 3,
  18. // };
  19. //
  20. // /* Allocate memory for the mesh and opt-out of normals. */
  21. // uint32_t num_indices;
  22. // uint32_t num_vertices;
  23. // par_octasphere_get_counts(&cfg, &num_indices, &num_vertices);
  24. // par_octasphere_mesh mesh = {
  25. // .positions = malloc(sizeof(float) * 3 * num_vertices),
  26. // .normals = NULL,
  27. // .texcoords = malloc(sizeof(float) * 2 * num_vertices),
  28. // .indices = malloc(sizeof(uint16_t) * num_indices),
  29. // };
  30. //
  31. // /* Generate vertex coordinates, UV's, and triangle indices. */
  32. // par_octasphere_populate(&cfg, &mesh);
  33. //
  34. // To generate a sphere: set width, height, and depth to 0 in your configuration.
  35. // To generate a capsule shape: set only two of these dimensions to 0.
  36. //
  37. // Distributed under the MIT License, see bottom of file.
  38. #ifndef PAR_OCTASPHERE_H
  39. #define PAR_OCTASPHERE_H
  40. #include <stdbool.h>
  41. #include <stdint.h>
  42. #ifdef __cplusplus
  43. extern "C" {
  44. #endif
  45. #define PAR_OCTASPHERE_MAX_SUBDIVISIONS 5
  46. typedef enum {
  47. PAR_OCTASPHERE_UV_LATLONG, // Classic sphere mapping with theta-phi.
  48. PAR_OCTASPHERE_UV_PTEX, // Each face in the control mesh gets its own patch.
  49. } par_octasphere_uv_mode;
  50. typedef enum {
  51. PAR_OCTASPHERE_NORMALS_SMOOTH,
  52. } par_octasphere_normals_mode;
  53. typedef struct {
  54. float corner_radius;
  55. float width;
  56. float height;
  57. float depth;
  58. int num_subdivisions;
  59. par_octasphere_uv_mode uv_mode;
  60. par_octasphere_normals_mode normals_mode;
  61. } par_octasphere_config;
  62. typedef struct {
  63. float* positions;
  64. float* normals;
  65. float* texcoords;
  66. uint16_t* indices;
  67. uint32_t num_indices;
  68. uint32_t num_vertices;
  69. } par_octasphere_mesh;
  70. // Computes the number of indices and vertices for the given octasphere config.
  71. void par_octasphere_get_counts(const par_octasphere_config* config, uint32_t* num_indices,
  72. uint32_t* num_vertices);
  73. // Populates a pre-allocated mesh structure with indices and vertices.
  74. void par_octasphere_populate(const par_octasphere_config* config, par_octasphere_mesh* mesh);
  75. #ifdef __cplusplus
  76. }
  77. #endif
  78. // -----------------------------------------------------------------------------
  79. // END PUBLIC API
  80. // -----------------------------------------------------------------------------
  81. #ifdef PAR_OCTASPHERE_IMPLEMENTATION
  82. #include <assert.h>
  83. #include <math.h>
  84. #include <memory.h> // for memcpy
  85. #define PARO_PI (3.14159265359)
  86. #define PARO_MIN(a, b) (a > b ? b : a)
  87. #define PARO_MAX(a, b) (a > b ? a : b)
  88. #define PARO_CLAMP(v, lo, hi) PARO_MAX(lo, PARO_MIN(hi, v))
  89. #define PARO_MAX_BOUNDARY_LENGTH ((1 << PAR_OCTASPHERE_MAX_SUBDIVISIONS) + 1)
  90. // Writes two triangles into the index buffer according to the given quad corners.
  91. static uint16_t* paro_write_quad(uint16_t* dst, uint16_t a, uint16_t b, uint16_t c, uint16_t d) {
  92. *dst++ = a;
  93. *dst++ = b;
  94. *dst++ = c;
  95. *dst++ = c;
  96. *dst++ = d;
  97. *dst++ = a;
  98. return dst;
  99. }
  100. // Copies the 4 verts at the given indices, appending them to the end of the vertex buffer.
  101. static void paro_write_quad_unwelded(float const* src_vertices, uint16_t** pindex_write_ptr,
  102. float** pvertex_write_ptr, uint16_t a, uint16_t b, uint16_t c,
  103. uint16_t d) {
  104. float* vertex_write_ptr = *pvertex_write_ptr;
  105. uint16_t* index_write_ptr = *pindex_write_ptr;
  106. const uint16_t first_index = (vertex_write_ptr - src_vertices) / 3;
  107. *index_write_ptr++ = first_index + 0;
  108. *index_write_ptr++ = first_index + 1;
  109. *index_write_ptr++ = first_index + 2;
  110. *index_write_ptr++ = first_index + 2;
  111. *index_write_ptr++ = first_index + 3;
  112. *index_write_ptr++ = first_index + 0;
  113. *pindex_write_ptr = index_write_ptr;
  114. for (int axis = 0; axis < 3; ++axis) *vertex_write_ptr++ = src_vertices[a * 3 + axis];
  115. for (int axis = 0; axis < 3; ++axis) *vertex_write_ptr++ = src_vertices[b * 3 + axis];
  116. for (int axis = 0; axis < 3; ++axis) *vertex_write_ptr++ = src_vertices[c * 3 + axis];
  117. for (int axis = 0; axis < 3; ++axis) *vertex_write_ptr++ = src_vertices[d * 3 + axis];
  118. *pvertex_write_ptr = vertex_write_ptr;
  119. }
  120. static void paro_write_quad_uv(float** puv_write_ptr, float x0, float y0, float x1, float y1, int a,
  121. int b, int c, int d) {
  122. // clang-format off
  123. float corners[4][2];
  124. corners[0][0] = x0; corners[0][1] = y0;
  125. corners[1][0] = x0; corners[1][1] = y1;
  126. corners[2][0] = x1; corners[2][1] = y0;
  127. corners[3][0] = x1; corners[3][1] = y1;
  128. float* uv = *puv_write_ptr;
  129. *uv++ = corners[a][0]; *uv++ = corners[a][1];
  130. *uv++ = corners[b][0]; *uv++ = corners[b][1];
  131. *uv++ = corners[c][0]; *uv++ = corners[c][1];
  132. *uv++ = corners[c][0]; *uv++ = corners[d][1];
  133. // clang-format on
  134. *puv_write_ptr = uv;
  135. }
  136. static void paro_write_ui3(uint16_t* dst, int index, uint16_t a, uint16_t b, uint16_t c) {
  137. dst[index * 3 + 0] = a;
  138. dst[index * 3 + 1] = b;
  139. dst[index * 3 + 2] = c;
  140. }
  141. static float* paro_write_f3(float* dst, const float src[3]) {
  142. dst[0] = src[0];
  143. dst[1] = src[1];
  144. dst[2] = src[2];
  145. return dst + 3;
  146. }
  147. static void paro_copy(float dst[3], const float src[3]) {
  148. dst[0] = src[0];
  149. dst[1] = src[1];
  150. dst[2] = src[2];
  151. }
  152. static float paro_dot(const float a[3], const float b[3]) {
  153. return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
  154. }
  155. static void paro_add(float result[3], float const a[3], float const b[3]) {
  156. result[0] = a[0] + b[0];
  157. result[1] = a[1] + b[1];
  158. result[2] = a[2] + b[2];
  159. }
  160. static void paro_normalize(float v[3]) {
  161. float lsqr = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  162. if (lsqr > 0) {
  163. v[0] /= lsqr;
  164. v[1] /= lsqr;
  165. v[2] /= lsqr;
  166. }
  167. }
  168. static void paro_cross(float result[3], float const a[3], float const b[3]) {
  169. float x = (a[1] * b[2]) - (a[2] * b[1]);
  170. float y = (a[2] * b[0]) - (a[0] * b[2]);
  171. float z = (a[0] * b[1]) - (a[1] * b[0]);
  172. result[0] = x;
  173. result[1] = y;
  174. result[2] = z;
  175. }
  176. static void paro_scale(float dst[3], float v) {
  177. dst[0] *= v;
  178. dst[1] *= v;
  179. dst[2] *= v;
  180. }
  181. static void paro_scaled(float dst[3], const float src[3], float v) {
  182. dst[0] = src[0] * v;
  183. dst[1] = src[1] * v;
  184. dst[2] = src[2] * v;
  185. }
  186. static void paro_quat_from_rotation(float quat[4], const float axis[3], float radians) {
  187. paro_copy(quat, axis);
  188. paro_normalize(quat);
  189. paro_scale(quat, sin(0.5 * radians));
  190. quat[3] = cos(0.5 * radians);
  191. }
  192. static void paro_quat_from_eulers(float quat[4], const float eulers[3]) {
  193. const float roll = eulers[0];
  194. const float pitch = eulers[1];
  195. const float yaw = eulers[2];
  196. const float halfRoll = roll * 0.5;
  197. const float sR = sin(halfRoll);
  198. const float cR = cos(halfRoll);
  199. const float halfPitch = pitch * 0.5;
  200. const float sP = sin(halfPitch);
  201. const float cP = cos(halfPitch);
  202. const float halfYaw = yaw * 0.5;
  203. const float sY = sin(halfYaw);
  204. const float cY = cos(halfYaw);
  205. quat[0] = (sR * cP * cY) + (cR * sP * sY);
  206. quat[1] = (cR * sP * cY) - (sR * cP * sY);
  207. quat[2] = (cR * cP * sY) + (sR * sP * cY);
  208. quat[3] = (cR * cP * cY) - (sR * sP * sY);
  209. }
  210. static void paro_quat_rotate_vector(float dst[3], const float quat[4], const float src[3]) {
  211. float t[3];
  212. paro_cross(t, quat, src);
  213. paro_scale(t, 2.0);
  214. float p[3];
  215. paro_cross(p, quat, t);
  216. paro_scaled(dst, t, quat[3]);
  217. paro_add(dst, dst, src);
  218. paro_add(dst, dst, p);
  219. }
  220. static float* paro_write_geodesic(float* dst, const float point_a[3], const float point_b[3],
  221. int num_segments) {
  222. dst = paro_write_f3(dst, point_a);
  223. if (num_segments == 0) {
  224. return dst;
  225. }
  226. const float angle_between_endpoints = acos(paro_dot(point_a, point_b));
  227. const float dtheta = angle_between_endpoints / num_segments;
  228. float rotation_axis[3], quat[4];
  229. paro_cross(rotation_axis, point_a, point_b);
  230. for (int point_index = 1; point_index < num_segments; point_index++, dst += 3) {
  231. paro_quat_from_rotation(quat, rotation_axis, dtheta * point_index);
  232. paro_quat_rotate_vector(dst, quat, point_a);
  233. }
  234. return paro_write_f3(dst, point_b);
  235. }
  236. // Find the vertex indices along each of the first patch's 3 edges.
  237. static void paro_get_patch_boundaries(const par_octasphere_config* config,
  238. uint16_t boundaries[3][PARO_MAX_BOUNDARY_LENGTH]) {
  239. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  240. const int n = (1 << ndivisions) + 1;
  241. int a = 0, b = 0, c = 0, row;
  242. uint16_t j0 = 0;
  243. for (int col_index = 0; col_index < n - 1; col_index++) {
  244. int col_height = n - 1 - col_index;
  245. uint16_t j1 = j0 + 1;
  246. boundaries[0][a++] = j0;
  247. for (row = 0; row < col_height - 1; row++) {
  248. if (col_height == n - 1) {
  249. boundaries[2][c++] = j0 + row;
  250. }
  251. }
  252. if (col_height == n - 1) {
  253. boundaries[2][c++] = j0 + row;
  254. boundaries[2][c++] = j1 + row;
  255. }
  256. boundaries[1][b++] = j1 + row;
  257. j0 += col_height + 1;
  258. }
  259. boundaries[0][a] = boundaries[1][b] = j0 + row;
  260. }
  261. static void paro_add_quads_ptex(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  262. assert(config->uv_mode == PAR_OCTASPHERE_UV_PTEX);
  263. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  264. const int n = (1 << ndivisions) + 1;
  265. const int verts_per_patch = n * (n + 1) / 2;
  266. #warning "TODO: Compute normals for newly added verts."
  267. // - 4*(n-1) quads between the 4 top patches.
  268. // - 4*(n-1) quads between the 4 bottom patches.
  269. // - 4*(n-1) quads between the top and bottom patches.
  270. // - 6 quads to fill "holes" in each cuboid face.
  271. const int num_connection_quads = (4 + 4 + 4) * (n - 1) + 6;
  272. uint16_t boundaries[3][PARO_MAX_BOUNDARY_LENGTH];
  273. paro_get_patch_boundaries(config, boundaries);
  274. uint16_t* ind_write_ptr = mesh->indices + mesh->num_indices;
  275. float* pos_write_ptr = mesh->positions + mesh->num_vertices * 3;
  276. // Go around the top half.
  277. for (int patch = 0; patch < 4; patch++) {
  278. const int next_patch = (patch + 1) % 4;
  279. const uint16_t* boundary_a = boundaries[1];
  280. const uint16_t* boundary_b = boundaries[0];
  281. const uint16_t offset_a = verts_per_patch * patch;
  282. const uint16_t offset_b = verts_per_patch * next_patch;
  283. for (int i = 0; i < n - 1; i++) {
  284. const uint16_t a = boundary_a[i] + offset_a;
  285. const uint16_t b = boundary_b[i] + offset_b;
  286. const uint16_t c = boundary_a[i + 1] + offset_a;
  287. const uint16_t d = boundary_b[i + 1] + offset_b;
  288. paro_write_quad_unwelded(mesh->positions, &ind_write_ptr, &pos_write_ptr, a, b, d, c);
  289. }
  290. }
  291. // Go around the bottom half.
  292. for (int patch = 4; patch < 8; patch++) {
  293. const int next_patch = 4 + (patch + 1) % 4;
  294. const uint16_t* boundary_a = boundaries[0];
  295. const uint16_t* boundary_b = boundaries[2];
  296. const uint16_t offset_a = verts_per_patch * patch;
  297. const uint16_t offset_b = verts_per_patch * next_patch;
  298. for (int i = 0; i < n - 1; i++) {
  299. const uint16_t a = boundary_a[i] + offset_a;
  300. const uint16_t b = boundary_b[i] + offset_b;
  301. const uint16_t c = boundary_a[i + 1] + offset_a;
  302. const uint16_t d = boundary_b[i + 1] + offset_b;
  303. paro_write_quad_unwelded(mesh->positions, &ind_write_ptr, &pos_write_ptr, d, b, a, c);
  304. }
  305. }
  306. // Connect the top and bottom halves.
  307. for (int patch = 0; patch < 4; patch++) {
  308. const int next_patch = 4 + (4 - patch) % 4;
  309. const uint16_t* boundary_a = boundaries[2];
  310. const uint16_t* boundary_b = boundaries[1];
  311. const uint16_t offset_a = verts_per_patch * patch;
  312. const uint16_t offset_b = verts_per_patch * next_patch;
  313. for (int i = 0; i < n - 1; i++) {
  314. const uint16_t a = boundary_a[i] + offset_a;
  315. const uint16_t b = boundary_b[n - 1 - i] + offset_b;
  316. const uint16_t c = boundary_a[i + 1] + offset_a;
  317. const uint16_t d = boundary_b[n - 1 - i - 1] + offset_b;
  318. paro_write_quad_unwelded(mesh->positions, &ind_write_ptr, &pos_write_ptr, a, b, d, c);
  319. }
  320. }
  321. // Fill in the top and bottom holes.
  322. uint16_t a, b, c, d;
  323. a = boundaries[0][n - 1];
  324. b = a + verts_per_patch;
  325. c = b + verts_per_patch;
  326. d = c + verts_per_patch;
  327. paro_write_quad_unwelded(mesh->positions, &ind_write_ptr, &pos_write_ptr, a, b, c, d);
  328. a = boundaries[2][0] + verts_per_patch * 4;
  329. b = a + verts_per_patch;
  330. c = b + verts_per_patch;
  331. d = c + verts_per_patch;
  332. paro_write_quad_unwelded(mesh->positions, &ind_write_ptr, &pos_write_ptr, a, b, c, d);
  333. // Fill in the side holes.
  334. const int sides[4][2] = {{7, 0}, {1, 2}, {3, 4}, {5, 6}};
  335. for (int side = 0; side < 4; side++) {
  336. int patch_index, patch, next_patch;
  337. uint16_t *boundary_a, *boundary_b;
  338. uint16_t offset_a, offset_b;
  339. uint16_t a, b;
  340. patch_index = sides[side][0];
  341. patch = patch_index / 2;
  342. next_patch = 4 + (4 - patch) % 4;
  343. offset_a = verts_per_patch * patch;
  344. offset_b = verts_per_patch * next_patch;
  345. boundary_a = boundaries[2];
  346. boundary_b = boundaries[1];
  347. if (patch_index % 2 == 0) {
  348. a = boundary_a[0] + offset_a;
  349. b = boundary_b[n - 1] + offset_b;
  350. } else {
  351. a = boundary_a[n - 1] + offset_a;
  352. b = boundary_b[0] + offset_b;
  353. }
  354. uint16_t c, d;
  355. patch_index = sides[side][1];
  356. patch = patch_index / 2;
  357. next_patch = 4 + (4 - patch) % 4;
  358. offset_a = verts_per_patch * patch;
  359. offset_b = verts_per_patch * next_patch;
  360. boundary_a = boundaries[2];
  361. boundary_b = boundaries[1];
  362. if (patch_index % 2 == 0) {
  363. c = boundary_a[0] + offset_a;
  364. d = boundary_b[n - 1] + offset_b;
  365. } else {
  366. c = boundary_a[n - 1] + offset_a;
  367. d = boundary_b[0] + offset_b;
  368. }
  369. paro_write_quad_unwelded(mesh->positions, &ind_write_ptr, &pos_write_ptr, a, b, d, c);
  370. }
  371. mesh->num_indices += num_connection_quads * 6;
  372. mesh->num_vertices += num_connection_quads * 4;
  373. #ifndef NDEBUG
  374. assert(mesh->num_indices == ind_write_ptr - mesh->indices);
  375. assert(mesh->num_vertices == (pos_write_ptr - mesh->positions) / 3);
  376. uint32_t expected_indices;
  377. uint32_t expected_vertices;
  378. par_octasphere_get_counts(config, &expected_indices, &expected_vertices);
  379. assert(mesh->num_indices == expected_indices);
  380. assert(mesh->num_vertices == expected_vertices);
  381. #endif
  382. }
  383. // This is similar to paro_add_quads_ptex except that verts between patches and quads are welded.
  384. static void paro_add_quads(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  385. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  386. const int n = (1 << ndivisions) + 1;
  387. const int verts_per_patch = n * (n + 1) / 2;
  388. uint16_t boundaries[3][PARO_MAX_BOUNDARY_LENGTH];
  389. paro_get_patch_boundaries(config, boundaries);
  390. uint16_t* write_ptr = mesh->indices + mesh->num_indices;
  391. const uint16_t* begin_ptr = write_ptr;
  392. // Go around the top half.
  393. for (int patch = 0; patch < 4; patch++) {
  394. const int next_patch = (patch + 1) % 4;
  395. const uint16_t* boundary_a = boundaries[1];
  396. const uint16_t* boundary_b = boundaries[0];
  397. const uint16_t offset_a = verts_per_patch * patch;
  398. const uint16_t offset_b = verts_per_patch * next_patch;
  399. for (int i = 0; i < n - 1; i++) {
  400. const uint16_t a = boundary_a[i] + offset_a;
  401. const uint16_t b = boundary_b[i] + offset_b;
  402. const uint16_t c = boundary_a[i + 1] + offset_a;
  403. const uint16_t d = boundary_b[i + 1] + offset_b;
  404. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  405. }
  406. }
  407. // Go around the bottom half.
  408. for (int patch = 4; patch < 8; patch++) {
  409. const int next_patch = 4 + (patch + 1) % 4;
  410. const uint16_t* boundary_a = boundaries[0];
  411. const uint16_t* boundary_b = boundaries[2];
  412. const uint16_t offset_a = verts_per_patch * patch;
  413. const uint16_t offset_b = verts_per_patch * next_patch;
  414. for (int i = 0; i < n - 1; i++) {
  415. const uint16_t a = boundary_a[i] + offset_a;
  416. const uint16_t b = boundary_b[i] + offset_b;
  417. const uint16_t c = boundary_a[i + 1] + offset_a;
  418. const uint16_t d = boundary_b[i + 1] + offset_b;
  419. write_ptr = paro_write_quad(write_ptr, d, b, a, c);
  420. }
  421. }
  422. // Connect the top and bottom halves.
  423. for (int patch = 0; patch < 4; patch++) {
  424. const int next_patch = 4 + (4 - patch) % 4;
  425. const uint16_t* boundary_a = boundaries[2];
  426. const uint16_t* boundary_b = boundaries[1];
  427. const uint16_t offset_a = verts_per_patch * patch;
  428. const uint16_t offset_b = verts_per_patch * next_patch;
  429. for (int i = 0; i < n - 1; i++) {
  430. const uint16_t a = boundary_a[i] + offset_a;
  431. const uint16_t b = boundary_b[n - 1 - i] + offset_b;
  432. const uint16_t c = boundary_a[i + 1] + offset_a;
  433. const uint16_t d = boundary_b[n - 1 - i - 1] + offset_b;
  434. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  435. }
  436. }
  437. // Fill in the top and bottom holes.
  438. uint16_t a, b, c, d;
  439. a = boundaries[0][n - 1];
  440. b = a + verts_per_patch;
  441. c = b + verts_per_patch;
  442. d = c + verts_per_patch;
  443. write_ptr = paro_write_quad(write_ptr, a, b, c, d);
  444. a = boundaries[2][0] + verts_per_patch * 4;
  445. b = a + verts_per_patch;
  446. c = b + verts_per_patch;
  447. d = c + verts_per_patch;
  448. write_ptr = paro_write_quad(write_ptr, a, b, c, d);
  449. // Fill in the side holes.
  450. const int sides[4][2] = {{7, 0}, {1, 2}, {3, 4}, {5, 6}};
  451. for (int side = 0; side < 4; side++) {
  452. int patch_index, patch, next_patch;
  453. uint16_t *boundary_a, *boundary_b;
  454. uint16_t offset_a, offset_b;
  455. uint16_t a, b;
  456. patch_index = sides[side][0];
  457. patch = patch_index / 2;
  458. next_patch = 4 + (4 - patch) % 4;
  459. offset_a = verts_per_patch * patch;
  460. offset_b = verts_per_patch * next_patch;
  461. boundary_a = boundaries[2];
  462. boundary_b = boundaries[1];
  463. if (patch_index % 2 == 0) {
  464. a = boundary_a[0] + offset_a;
  465. b = boundary_b[n - 1] + offset_b;
  466. } else {
  467. a = boundary_a[n - 1] + offset_a;
  468. b = boundary_b[0] + offset_b;
  469. }
  470. uint16_t c, d;
  471. patch_index = sides[side][1];
  472. patch = patch_index / 2;
  473. next_patch = 4 + (4 - patch) % 4;
  474. offset_a = verts_per_patch * patch;
  475. offset_b = verts_per_patch * next_patch;
  476. boundary_a = boundaries[2];
  477. boundary_b = boundaries[1];
  478. if (patch_index % 2 == 0) {
  479. c = boundary_a[0] + offset_a;
  480. d = boundary_b[n - 1] + offset_b;
  481. } else {
  482. c = boundary_a[n - 1] + offset_a;
  483. d = boundary_b[0] + offset_b;
  484. }
  485. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  486. }
  487. mesh->num_indices += write_ptr - begin_ptr;
  488. #ifndef NDEBUG
  489. uint32_t expected_indices;
  490. uint32_t expected_vertices;
  491. par_octasphere_get_counts(config, &expected_indices, &expected_vertices);
  492. assert(mesh->num_indices == expected_indices);
  493. #endif
  494. }
  495. static void paro_populate_uvs_latlong(const par_octasphere_config* config,
  496. par_octasphere_mesh* mesh) {
  497. assert(config->uv_mode == PAR_OCTASPHERE_UV_LATLONG);
  498. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  499. const int n = (1 << ndivisions) + 1;
  500. const int verts_per_patch = n * (n + 1) / 2;
  501. const int total_vertices = verts_per_patch * 8;
  502. for (int i = 0; i < total_vertices; i++) {
  503. const int octant = i / verts_per_patch;
  504. const int relative_index = i % verts_per_patch;
  505. float* uv = mesh->texcoords + i * 2;
  506. const float* xyz = mesh->positions + i * 3;
  507. const float x = xyz[0], y = xyz[1], z = xyz[2];
  508. const float phi = -atan2(z, x);
  509. const float theta = acos(y);
  510. uv[0] = 0.5 * (phi / PARO_PI + 1.0);
  511. uv[1] = theta / PARO_PI;
  512. // Special case for the north pole.
  513. if (octant < 4 && relative_index == verts_per_patch - 1) {
  514. uv[0] = fmod(0.375 + 0.25 * octant, 1.0);
  515. uv[1] = 0;
  516. }
  517. // Special case for the south pole.
  518. if (octant >= 4 && relative_index == 0) {
  519. uv[0] = 0.375 - 0.25 * (octant - 4);
  520. uv[0] = uv[0] + uv[0] < 0 ? 1.0 : 0.0;
  521. uv[1] = 1.0;
  522. }
  523. // Adjust the prime meridian for proper wrapping.
  524. if ((octant == 2 || octant == 6) && uv[0] < 0.5) {
  525. uv[0] += 1.0;
  526. }
  527. }
  528. }
  529. static void paro_octahedral_proj(const float dir_in[3], float uv_out[2], int octant) {
  530. float dir[3];
  531. paro_copy(dir, dir_in);
  532. paro_scale(dir, 1.0f / (fabs(dir[0]) + fabs(dir[1]) + fabs(dir[2])));
  533. const float rev[2] = {
  534. fabs(dir[2]) - 1.0f,
  535. fabs(dir[0]) - 1.0f,
  536. };
  537. const float neg[2] = {
  538. dir[0] < 0 ? rev[0] : -rev[0],
  539. dir[2] < 0 ? rev[1] : -rev[1],
  540. };
  541. uv_out[0] = dir[1] < 0 ? neg[0] : dir[0];
  542. uv_out[1] = dir[1] < 0 ? neg[1] : dir[2];
  543. uv_out[0] = 0.5 * uv_out[0] + 0.5;
  544. uv_out[1] = 0.5 * uv_out[1] + 0.5;
  545. if (octant == 4) { // G
  546. if (uv_out[1] <= 0.5) uv_out[1] = 1.0 - uv_out[1];
  547. }
  548. if (octant == 5) { // E
  549. if (uv_out[1] <= 0.5) uv_out[1] = 1.0 - uv_out[1];
  550. if (uv_out[0] > 0.5) uv_out[0] = 1.0 - uv_out[0];
  551. }
  552. if (octant == 6) { // A
  553. if (uv_out[0] > 0.5) uv_out[0] = 1.0 - uv_out[0];
  554. }
  555. if (octant == 7) { // C
  556. if (uv_out[0] <= 0.5) uv_out[0] = 1.0 - uv_out[0];
  557. if (uv_out[1] > 0.5) uv_out[1] = 1.0 - uv_out[1];
  558. }
  559. }
  560. static void paro_populate_uvs_ptex_patches(const par_octasphere_config* config,
  561. par_octasphere_mesh* mesh) {
  562. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  563. const int n = (1 << ndivisions) + 1;
  564. const int verts_per_patch = n * (n + 1) / 2;
  565. assert(config->uv_mode == PAR_OCTASPHERE_UV_PTEX);
  566. const float* pos_read_ptr = mesh->positions;
  567. float* uvs_write_ptr = mesh->texcoords;
  568. for (int i = 0; i < verts_per_patch * 4; i++, pos_read_ptr += 3, uvs_write_ptr += 2) {
  569. paro_octahedral_proj(pos_read_ptr, uvs_write_ptr, 0); // B, D, F, H
  570. uvs_write_ptr[0] *= 2.0 / 11.0;
  571. }
  572. for (int i = 0; i < verts_per_patch; i++, pos_read_ptr += 3, uvs_write_ptr += 2) {
  573. paro_octahedral_proj(pos_read_ptr, uvs_write_ptr, 4); // G
  574. uvs_write_ptr[0] *= 2.0 / 11.0;
  575. }
  576. for (int i = 0; i < verts_per_patch; i++, pos_read_ptr += 3, uvs_write_ptr += 2) {
  577. paro_octahedral_proj(pos_read_ptr, uvs_write_ptr, 5); // E
  578. uvs_write_ptr[0] *= 2.0 / 11.0;
  579. }
  580. for (int i = 0; i < verts_per_patch; i++, pos_read_ptr += 3, uvs_write_ptr += 2) {
  581. paro_octahedral_proj(pos_read_ptr, uvs_write_ptr, 6); // A
  582. uvs_write_ptr[0] *= 2.0 / 11.0;
  583. }
  584. for (int i = 0; i < verts_per_patch; i++, pos_read_ptr += 3, uvs_write_ptr += 2) {
  585. paro_octahedral_proj(pos_read_ptr, uvs_write_ptr, 7); // C
  586. uvs_write_ptr[0] *= 2.0 / 11.0;
  587. }
  588. }
  589. static void paro_populate_uvs_ptex_quads(const par_octasphere_config* config,
  590. par_octasphere_mesh* mesh) {
  591. assert(config->uv_mode == PAR_OCTASPHERE_UV_PTEX);
  592. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  593. const int n = (1 << ndivisions) + 1;
  594. const int verts_per_patch = n * (n + 1) / 2;
  595. float* uvs_write_ptr = mesh->texcoords + verts_per_patch * 8 * 2;
  596. for (int i = verts_per_patch * 8; i < mesh->num_vertices; i++, uvs_write_ptr += 2) {
  597. uvs_write_ptr[0] = 0;
  598. uvs_write_ptr[1] = 0;
  599. }
  600. uvs_write_ptr = mesh->texcoords + verts_per_patch * 8 * 2;
  601. float x = 0;
  602. float y = 0;
  603. const float dx = 1.0 / (n - 1);
  604. const int a = 0, b = 1, c = 2, d = 3;
  605. // Go around the top half. (I K M O)
  606. for (int patch = 0; patch < 4; patch++) {
  607. for (int i = 0; i < n - 1; i++) {
  608. paro_write_quad_uv(&uvs_write_ptr, x, 0, x + dx, 1, a, b, d, c);
  609. x = x + dx;
  610. }
  611. }
  612. // Go around the bottom half. (Q S U W)
  613. for (int patch = 0; patch < 4; patch++) {
  614. for (int i = 0; i < n - 1; i++) {
  615. paro_write_quad_uv(&uvs_write_ptr, x, y, x + 1, y + dx, d, c, a, b);
  616. y = y + dx;
  617. }
  618. y = 0;
  619. x++;
  620. }
  621. // Connect the top and bottom halves. (Y J L N)
  622. for (int patch = 0; patch < 4; patch++) {
  623. for (int i = 0; i < n - 1; i++) {
  624. paro_write_quad_uv(&uvs_write_ptr, x, 0, x + dx, 1, a, b, d, c);
  625. x = x + dx;
  626. }
  627. }
  628. // Normalize the coordinates.
  629. uvs_write_ptr = mesh->texcoords + verts_per_patch * 8 * 2;
  630. for (int patch = 0; patch < 12; patch++) {
  631. for (int i = 0; i < 4 * (n - 1); i++) {
  632. if (patch >= 9) {
  633. uvs_write_ptr[0] -= 9;
  634. uvs_write_ptr[1] += 1;
  635. }
  636. uvs_write_ptr[0] = 2.0 / 11.0 + uvs_write_ptr[0] / 11.0;
  637. uvs_write_ptr[1] = uvs_write_ptr[1] / 2.0;
  638. uvs_write_ptr += 2;
  639. }
  640. }
  641. // Central quads of the six cube faces (P R T V X Z)
  642. // clang-format off
  643. paro_write_quad_uv(&uvs_write_ptr, x, 1, x + 1, 2, d, c, a, b); x++;
  644. paro_write_quad_uv(&uvs_write_ptr, x, 1, x + 1, 2, d, c, a, b); x++;
  645. paro_write_quad_uv(&uvs_write_ptr, x, 1, x + 1, 2, d, c, a, b); x++;
  646. paro_write_quad_uv(&uvs_write_ptr, x, 1, x + 1, 2, d, c, a, b); x++;
  647. paro_write_quad_uv(&uvs_write_ptr, x, 1, x + 1, 2, d, c, a, b); x++;
  648. paro_write_quad_uv(&uvs_write_ptr, x, 1, x + 1, 2, d, c, a, b); x++;
  649. // clang-format on
  650. // Normalize the coordinates.
  651. uvs_write_ptr -= 4 * 6 * 2;
  652. for (int i = 0; i < 6 * 4; i++) {
  653. uvs_write_ptr[0] -= 9;
  654. uvs_write_ptr[0] = 2.0 / 11.0 + uvs_write_ptr[0] / 11.0;
  655. uvs_write_ptr[1] = uvs_write_ptr[1] / 2.0;
  656. uvs_write_ptr += 2;
  657. }
  658. }
  659. void par_octasphere_get_counts(const par_octasphere_config* config, uint32_t* num_indices,
  660. uint32_t* num_vertices) {
  661. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  662. const int n = (1 << ndivisions) + 1;
  663. const int verts_per_patch = n * (n + 1) / 2;
  664. const int triangles_per_patch = (n - 2) * (n - 1) + n - 1;
  665. // - 4*(n-1) quads between the 4 top patches.
  666. // - 4*(n-1) quads between the 4 bottom patches.
  667. // - 4*(n-1) quads between the top and bottom patches.
  668. // - 6 quads to fill "holes" in each cuboid face.
  669. const int num_connection_quads = (4 + 4 + 4) * (n - 1) + 6;
  670. *num_indices = (triangles_per_patch * 8 + num_connection_quads * 2) * 3;
  671. *num_vertices = verts_per_patch * 8;
  672. if (config->uv_mode == PAR_OCTASPHERE_UV_PTEX) {
  673. *num_vertices += num_connection_quads * 4;
  674. }
  675. }
  676. void par_octasphere_populate(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  677. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  678. const int n = (1 << ndivisions) + 1;
  679. const int verts_per_patch = n * (n + 1) / 2;
  680. const float r2 = config->corner_radius * 2;
  681. const float w = PARO_MAX(config->width, r2);
  682. const float h = PARO_MAX(config->height, r2);
  683. const float d = PARO_MAX(config->depth, r2);
  684. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  685. const int triangles_per_patch = (n - 2) * (n - 1) + n - 1;
  686. const int total_vertices = verts_per_patch * 8;
  687. // START TESSELLATION OF SINGLE PATCH (one-eighth of the octasphere)
  688. float* write_ptr = mesh->positions;
  689. for (int i = 0; i < n; i++) {
  690. const float theta = PARO_PI * 0.5 * i / (n - 1);
  691. const float point_a[] = {0, sinf(theta), cosf(theta)};
  692. const float point_b[] = {cosf(theta), sinf(theta), 0};
  693. const int num_segments = n - 1 - i;
  694. write_ptr = paro_write_geodesic(write_ptr, point_a, point_b, num_segments);
  695. }
  696. int f = 0, j0 = 0;
  697. uint16_t* faces = mesh->indices;
  698. for (int col_index = 0; col_index < n - 1; col_index++) {
  699. const int col_height = n - 1 - col_index;
  700. const int j1 = j0 + 1;
  701. const int j2 = j0 + col_height + 1;
  702. const int j3 = j0 + col_height + 2;
  703. for (int row = 0; row < col_height - 1; row++) {
  704. paro_write_ui3(faces, f++, j0 + row, j1 + row, j2 + row);
  705. paro_write_ui3(faces, f++, j2 + row, j1 + row, j3 + row);
  706. }
  707. const int row = col_height - 1;
  708. paro_write_ui3(faces, f++, j0 + row, j1 + row, j2 + row);
  709. j0 = j2;
  710. }
  711. // END TESSELLATION OF SINGLE PATCH
  712. // START 8-WAY CLONE OF PATCH
  713. // clang-format off
  714. float euler_angles[8][3] = {
  715. {0, 0, 0}, {0, 1, 0}, {0, 2, 0}, {0, 3, 0},
  716. {1, 0, 0}, {1, 0, 1}, {1, 0, 2}, {1, 0, 3},
  717. };
  718. // clang-format on
  719. for (int octant = 1; octant < 8; octant++) {
  720. paro_scale(euler_angles[octant], PARO_PI * 0.5);
  721. float quat[4];
  722. paro_quat_from_eulers(quat, euler_angles[octant]);
  723. float* dst = mesh->positions + octant * verts_per_patch * 3;
  724. const float* src = mesh->positions;
  725. for (int vindex = 0; vindex < verts_per_patch; vindex++, dst += 3, src += 3) {
  726. paro_quat_rotate_vector(dst, quat, src);
  727. }
  728. }
  729. for (int octant = 1; octant < 8; octant++) {
  730. const int indices_per_patch = triangles_per_patch * 3;
  731. uint16_t* dst = mesh->indices + octant * indices_per_patch;
  732. const uint16_t* src = mesh->indices;
  733. const uint16_t offset = verts_per_patch * octant;
  734. for (int iindex = 0; iindex < indices_per_patch; ++iindex) {
  735. dst[iindex] = src[iindex] + offset;
  736. }
  737. }
  738. // END 8-WAY CLONE OF PATCH
  739. if (mesh->texcoords && config->uv_mode == PAR_OCTASPHERE_UV_LATLONG) {
  740. paro_populate_uvs_latlong(config, mesh);
  741. }
  742. // The following memcpy for normals works because these patches are spherical in nature, and we
  743. // have not yet scaled and translated anything. Note that in PTEX mode, we need to populate
  744. // additional normals for the quads.
  745. if (mesh->normals && config->normals_mode == PAR_OCTASPHERE_NORMALS_SMOOTH) {
  746. memcpy(mesh->normals, mesh->positions, sizeof(float) * 3 * total_vertices);
  747. }
  748. if (config->corner_radius != 1.0) {
  749. for (int i = 0; i < total_vertices; i++) {
  750. float* xyz = mesh->positions + i * 3;
  751. xyz[0] *= config->corner_radius;
  752. xyz[1] *= config->corner_radius;
  753. xyz[2] *= config->corner_radius;
  754. }
  755. }
  756. mesh->num_indices = triangles_per_patch * 8 * 3;
  757. mesh->num_vertices = total_vertices;
  758. if (config->uv_mode == PAR_OCTASPHERE_UV_PTEX && mesh->texcoords) {
  759. paro_populate_uvs_ptex_patches(config, mesh);
  760. }
  761. for (int i = 0; i < total_vertices; i++) {
  762. float* xyz = mesh->positions + i * 3;
  763. const int octant = i / verts_per_patch;
  764. const float sx = (octant < 2 || octant == 4 || octant == 7) ? +1 : -1;
  765. const float sy = octant < 4 ? +1 : -1;
  766. const float sz = (octant == 0 || octant == 3 || octant == 4 || octant == 5) ? +1 : -1;
  767. xyz[0] += tx * sx;
  768. xyz[1] += ty * sy;
  769. xyz[2] += tz * sz;
  770. }
  771. if (config->uv_mode == PAR_OCTASPHERE_UV_PTEX) {
  772. paro_add_quads_ptex(config, mesh);
  773. if (mesh->texcoords) {
  774. paro_populate_uvs_ptex_quads(config, mesh);
  775. }
  776. } else {
  777. paro_add_quads(config, mesh);
  778. }
  779. }
  780. #endif // PAR_OCTASPHERE_IMPLEMENTATION
  781. #endif // PAR_OCTASPHERE_H
  782. // par_octasphere is distributed under the MIT license:
  783. //
  784. // Copyright (c) 2019 Philip Rideout
  785. //
  786. // Permission is hereby granted, free of charge, to any person obtaining a copy
  787. // of this software and associated documentation files (the "Software"), to deal
  788. // in the Software without restriction, including without limitation the rights
  789. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  790. // copies of the Software, and to permit persons to whom the Software is
  791. // furnished to do so, subject to the following conditions:
  792. //
  793. // The above copyright notice and this permission notice shall be included in
  794. // all copies or substantial portions of the Software.
  795. //
  796. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  797. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  798. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  799. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  800. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  801. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  802. // SOFTWARE.