par_octasphere.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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,
  48. } par_octasphere_uv_mode;
  49. typedef enum {
  50. PAR_OCTASPHERE_NORMALS_SMOOTH,
  51. } par_octasphere_normals_mode;
  52. typedef struct {
  53. float corner_radius;
  54. float width;
  55. float height;
  56. float depth;
  57. int num_subdivisions;
  58. par_octasphere_uv_mode uv_mode;
  59. par_octasphere_normals_mode normals_mode;
  60. } par_octasphere_config;
  61. typedef struct {
  62. float* positions;
  63. float* normals;
  64. float* texcoords;
  65. uint16_t* indices;
  66. uint32_t num_indices;
  67. uint32_t num_vertices;
  68. } par_octasphere_mesh;
  69. // Computes the maximum possible number of indices and vertices for the given octasphere config.
  70. void par_octasphere_get_counts(const par_octasphere_config* config, uint32_t* num_indices,
  71. uint32_t* num_vertices);
  72. // Populates a pre-allocated mesh structure with indices and vertices.
  73. void par_octasphere_populate(const par_octasphere_config* config, par_octasphere_mesh* mesh);
  74. #ifdef __cplusplus
  75. }
  76. #endif
  77. // -----------------------------------------------------------------------------
  78. // END PUBLIC API
  79. // -----------------------------------------------------------------------------
  80. #ifdef PAR_OCTASPHERE_IMPLEMENTATION
  81. #include <assert.h>
  82. #include <math.h>
  83. #include <memory.h> // for memcpy
  84. #define PARO_PI (3.14159265359)
  85. #define PARO_MIN(a, b) (a > b ? b : a)
  86. #define PARO_MAX(a, b) (a > b ? a : b)
  87. #define PARO_CLAMP(v, lo, hi) PARO_MAX(lo, PARO_MIN(hi, v))
  88. #define PARO_MAX_BOUNDARY_LENGTH ((1 << PAR_OCTASPHERE_MAX_SUBDIVISIONS) + 1)
  89. #ifndef PARO_CONSTANT_TOPOLOGY
  90. #define PARO_CONSTANT_TOPOLOGY 1
  91. #endif
  92. static uint16_t* paro_write_quad(uint16_t* dst, uint16_t a, uint16_t b, uint16_t c, uint16_t d) {
  93. *dst++ = a;
  94. *dst++ = b;
  95. *dst++ = c;
  96. *dst++ = c;
  97. *dst++ = d;
  98. *dst++ = a;
  99. return dst;
  100. }
  101. static void paro_write_ui3(uint16_t* dst, int index, uint16_t a, uint16_t b, uint16_t c) {
  102. dst[index * 3 + 0] = a;
  103. dst[index * 3 + 1] = b;
  104. dst[index * 3 + 2] = c;
  105. }
  106. static float* paro_write_f3(float* dst, const float src[3]) {
  107. dst[0] = src[0];
  108. dst[1] = src[1];
  109. dst[2] = src[2];
  110. return dst + 3;
  111. }
  112. static void paro_copy(float dst[3], const float src[3]) {
  113. dst[0] = src[0];
  114. dst[1] = src[1];
  115. dst[2] = src[2];
  116. }
  117. static float paro_dot(const float a[3], const float b[3]) {
  118. return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
  119. }
  120. static void paro_add(float result[3], float const a[3], float const b[3]) {
  121. result[0] = a[0] + b[0];
  122. result[1] = a[1] + b[1];
  123. result[2] = a[2] + b[2];
  124. }
  125. static void paro_normalize(float v[3]) {
  126. float lsqr = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  127. if (lsqr > 0) {
  128. v[0] /= lsqr;
  129. v[1] /= lsqr;
  130. v[2] /= lsqr;
  131. }
  132. }
  133. static void paro_cross(float result[3], float const a[3], float const b[3]) {
  134. float x = (a[1] * b[2]) - (a[2] * b[1]);
  135. float y = (a[2] * b[0]) - (a[0] * b[2]);
  136. float z = (a[0] * b[1]) - (a[1] * b[0]);
  137. result[0] = x;
  138. result[1] = y;
  139. result[2] = z;
  140. }
  141. static void paro_scale(float dst[3], float v) {
  142. dst[0] *= v;
  143. dst[1] *= v;
  144. dst[2] *= v;
  145. }
  146. static void paro_scaled(float dst[3], const float src[3], float v) {
  147. dst[0] = src[0] * v;
  148. dst[1] = src[1] * v;
  149. dst[2] = src[2] * v;
  150. }
  151. static void paro_quat_from_rotation(float quat[4], const float axis[3], float radians) {
  152. paro_copy(quat, axis);
  153. paro_normalize(quat);
  154. paro_scale(quat, sin(0.5 * radians));
  155. quat[3] = cos(0.5 * radians);
  156. }
  157. static void paro_quat_from_eulers(float quat[4], const float eulers[3]) {
  158. const float roll = eulers[0];
  159. const float pitch = eulers[1];
  160. const float yaw = eulers[2];
  161. const float halfRoll = roll * 0.5;
  162. const float sR = sin(halfRoll);
  163. const float cR = cos(halfRoll);
  164. const float halfPitch = pitch * 0.5;
  165. const float sP = sin(halfPitch);
  166. const float cP = cos(halfPitch);
  167. const float halfYaw = yaw * 0.5;
  168. const float sY = sin(halfYaw);
  169. const float cY = cos(halfYaw);
  170. quat[0] = (sR * cP * cY) + (cR * sP * sY);
  171. quat[1] = (cR * sP * cY) - (sR * cP * sY);
  172. quat[2] = (cR * cP * sY) + (sR * sP * cY);
  173. quat[3] = (cR * cP * cY) - (sR * sP * sY);
  174. }
  175. static void paro_quat_rotate_vector(float dst[3], const float quat[4], const float src[3]) {
  176. float t[3];
  177. paro_cross(t, quat, src);
  178. paro_scale(t, 2.0);
  179. float p[3];
  180. paro_cross(p, quat, t);
  181. paro_scaled(dst, t, quat[3]);
  182. paro_add(dst, dst, src);
  183. paro_add(dst, dst, p);
  184. }
  185. static float* paro_write_geodesic(float* dst, const float point_a[3], const float point_b[3],
  186. int num_segments) {
  187. dst = paro_write_f3(dst, point_a);
  188. if (num_segments == 0) {
  189. return dst;
  190. }
  191. const float angle_between_endpoints = acos(paro_dot(point_a, point_b));
  192. const float dtheta = angle_between_endpoints / num_segments;
  193. float rotation_axis[3], quat[4];
  194. paro_cross(rotation_axis, point_a, point_b);
  195. paro_normalize(rotation_axis);
  196. for (int point_index = 1; point_index < num_segments; point_index++, dst += 3) {
  197. paro_quat_from_rotation(quat, rotation_axis, dtheta * point_index);
  198. paro_quat_rotate_vector(dst, quat, point_a);
  199. }
  200. return paro_write_f3(dst, point_b);
  201. }
  202. void paro_add_quads(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  203. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  204. const int n = (1 << ndivisions) + 1;
  205. const int verts_per_patch = n * (n + 1) / 2;
  206. const float r2 = config->corner_radius * 2;
  207. const float w = PARO_MAX(config->width, r2);
  208. const float h = PARO_MAX(config->height, r2);
  209. const float d = PARO_MAX(config->depth, r2);
  210. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  211. // Find the vertex indices along each of the patch's 3 edges.
  212. uint16_t boundaries[3][PARO_MAX_BOUNDARY_LENGTH];
  213. int a = 0, b = 0, c = 0, row;
  214. uint16_t j0 = 0;
  215. for (int col_index = 0; col_index < n - 1; col_index++) {
  216. int col_height = n - 1 - col_index;
  217. uint16_t j1 = j0 + 1;
  218. boundaries[0][a++] = j0;
  219. for (row = 0; row < col_height - 1; row++) {
  220. if (col_height == n - 1) {
  221. boundaries[2][c++] = j0 + row;
  222. }
  223. }
  224. if (col_height == n - 1) {
  225. boundaries[2][c++] = j0 + row;
  226. boundaries[2][c++] = j1 + row;
  227. }
  228. boundaries[1][b++] = j1 + row;
  229. j0 += col_height + 1;
  230. }
  231. boundaries[0][a] = boundaries[1][b] = j0 + row;
  232. // If there is no rounding (i.e. this is a plain box), then clobber the existing indices.
  233. if (!PARO_CONSTANT_TOPOLOGY && config->corner_radius == 0) {
  234. mesh->num_indices = 0;
  235. }
  236. uint16_t* write_ptr = mesh->indices + mesh->num_indices;
  237. const uint16_t* begin_ptr = write_ptr;
  238. if (PARO_CONSTANT_TOPOLOGY || config->corner_radius > 0) {
  239. // Go around the top half.
  240. for (int patch = 0; patch < 4; patch++) {
  241. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 0 && tz == 0) continue;
  242. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 1 && tx == 0) continue;
  243. const int next_patch = (patch + 1) % 4;
  244. const uint16_t* boundary_a = boundaries[1];
  245. const uint16_t* boundary_b = boundaries[0];
  246. const uint16_t offset_a = verts_per_patch * patch;
  247. const uint16_t offset_b = verts_per_patch * next_patch;
  248. for (int i = 0; i < n - 1; i++) {
  249. const uint16_t a = boundary_a[i] + offset_a;
  250. const uint16_t b = boundary_b[i] + offset_b;
  251. const uint16_t c = boundary_a[i + 1] + offset_a;
  252. const uint16_t d = boundary_b[i + 1] + offset_b;
  253. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  254. }
  255. }
  256. // Go around the bottom half.
  257. for (int patch = 4; patch < 8; patch++) {
  258. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 0 && tx == 0) continue;
  259. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 1 && tz == 0) continue;
  260. const int next_patch = 4 + (patch + 1) % 4;
  261. const uint16_t* boundary_a = boundaries[0];
  262. const uint16_t* boundary_b = boundaries[2];
  263. const uint16_t offset_a = verts_per_patch * patch;
  264. const uint16_t offset_b = verts_per_patch * next_patch;
  265. for (int i = 0; i < n - 1; i++) {
  266. const uint16_t a = boundary_a[i] + offset_a;
  267. const uint16_t b = boundary_b[i] + offset_b;
  268. const uint16_t c = boundary_a[i + 1] + offset_a;
  269. const uint16_t d = boundary_b[i + 1] + offset_b;
  270. write_ptr = paro_write_quad(write_ptr, d, b, a, c);
  271. }
  272. }
  273. // Connect the top and bottom halves.
  274. if (PARO_CONSTANT_TOPOLOGY || ty > 0) {
  275. for (int patch = 0; patch < 4; patch++) {
  276. const int next_patch = 4 + (4 - patch) % 4;
  277. const uint16_t* boundary_a = boundaries[2];
  278. const uint16_t* boundary_b = boundaries[1];
  279. const uint16_t offset_a = verts_per_patch * patch;
  280. const uint16_t offset_b = verts_per_patch * next_patch;
  281. for (int i = 0; i < n - 1; i++) {
  282. const uint16_t a = boundary_a[i] + offset_a;
  283. const uint16_t b = boundary_b[n - 1 - i] + offset_b;
  284. const uint16_t c = boundary_a[i + 1] + offset_a;
  285. const uint16_t d = boundary_b[n - 1 - i - 1] + offset_b;
  286. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  287. }
  288. }
  289. }
  290. }
  291. // Fill in the top and bottom holes.
  292. if (PARO_CONSTANT_TOPOLOGY || tx > 0 || ty > 0) {
  293. uint16_t a, b, c, d;
  294. a = boundaries[0][n - 1];
  295. b = a + verts_per_patch;
  296. c = b + verts_per_patch;
  297. d = c + verts_per_patch;
  298. write_ptr = paro_write_quad(write_ptr, a, b, c, d);
  299. a = boundaries[2][0] + verts_per_patch * 4;
  300. b = a + verts_per_patch;
  301. c = b + verts_per_patch;
  302. d = c + verts_per_patch;
  303. write_ptr = paro_write_quad(write_ptr, a, b, c, d);
  304. }
  305. // Fill in the side holes.
  306. if (PARO_CONSTANT_TOPOLOGY || ty > 0) {
  307. const int sides[4][2] = {{7, 0}, {1, 2}, {3, 4}, {5, 6}};
  308. for (int side = 0; side < 4; side++) {
  309. int patch_index, patch, next_patch;
  310. uint16_t *boundary_a, *boundary_b;
  311. uint16_t offset_a, offset_b;
  312. uint16_t a, b;
  313. patch_index = sides[side][0];
  314. patch = patch_index / 2;
  315. next_patch = 4 + (4 - patch) % 4;
  316. offset_a = verts_per_patch * patch;
  317. offset_b = verts_per_patch * next_patch;
  318. boundary_a = boundaries[2];
  319. boundary_b = boundaries[1];
  320. if (patch_index % 2 == 0) {
  321. a = boundary_a[0] + offset_a;
  322. b = boundary_b[n - 1] + offset_b;
  323. } else {
  324. a = boundary_a[n - 1] + offset_a;
  325. b = boundary_b[0] + offset_b;
  326. }
  327. uint16_t c, d;
  328. patch_index = sides[side][1];
  329. patch = patch_index / 2;
  330. next_patch = 4 + (4 - patch) % 4;
  331. offset_a = verts_per_patch * patch;
  332. offset_b = verts_per_patch * next_patch;
  333. boundary_a = boundaries[2];
  334. boundary_b = boundaries[1];
  335. if (patch_index % 2 == 0) {
  336. c = boundary_a[0] + offset_a;
  337. d = boundary_b[n - 1] + offset_b;
  338. } else {
  339. c = boundary_a[n - 1] + offset_a;
  340. d = boundary_b[0] + offset_b;
  341. }
  342. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  343. }
  344. }
  345. mesh->num_indices += write_ptr - begin_ptr;
  346. #ifndef NDEBUG
  347. uint32_t expected_indices;
  348. uint32_t expected_vertices;
  349. par_octasphere_get_counts(config, &expected_indices, &expected_vertices);
  350. assert(mesh->num_indices <= expected_indices);
  351. #endif
  352. }
  353. void par_octasphere_get_counts(const par_octasphere_config* config, uint32_t* num_indices,
  354. uint32_t* num_vertices) {
  355. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  356. const int n = (1 << ndivisions) + 1;
  357. const int verts_per_patch = n * (n + 1) / 2;
  358. const float r2 = config->corner_radius * 2;
  359. const float w = PARO_MAX(config->width, r2);
  360. const float h = PARO_MAX(config->height, r2);
  361. const float d = PARO_MAX(config->depth, r2);
  362. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  363. const int triangles_per_patch = (n - 2) * (n - 1) + n - 1;
  364. // If this is a sphere, return early.
  365. if (tx == 0 && ty == 0 && tz == 0) {
  366. *num_indices = triangles_per_patch * 8 * 3;
  367. *num_vertices = verts_per_patch * 8;
  368. return;
  369. }
  370. // This is a cuboid, so account for the maximum number of possible quads.
  371. // - 4*(n-1) quads between the 4 top patches.
  372. // - 4*(n-1) quads between the 4 bottom patches.
  373. // - 4*(n-1) quads between the top and bottom patches.
  374. // - 6 quads to fill "holes" in each cuboid face.
  375. const int num_connection_quads = (4 + 4 + 4) * (n - 1) + 6;
  376. *num_indices = (triangles_per_patch * 8 + num_connection_quads * 2) * 3;
  377. *num_vertices = verts_per_patch * 8;
  378. }
  379. void par_octasphere_populate(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  380. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  381. const int n = (1 << ndivisions) + 1;
  382. const int verts_per_patch = n * (n + 1) / 2;
  383. const float r2 = config->corner_radius * 2;
  384. const float w = PARO_MAX(config->width, r2);
  385. const float h = PARO_MAX(config->height, r2);
  386. const float d = PARO_MAX(config->depth, r2);
  387. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  388. const int triangles_per_patch = (n - 2) * (n - 1) + n - 1;
  389. const int total_vertices = verts_per_patch * 8;
  390. // START TESSELLATION OF SINGLE PATCH (one-eighth of the octasphere)
  391. float* write_ptr = mesh->positions;
  392. for (int i = 0; i < n; i++) {
  393. const float theta = PARO_PI * 0.5 * i / (n - 1);
  394. const float point_a[] = {0, sinf(theta), cosf(theta)};
  395. const float point_b[] = {cosf(theta), sinf(theta), 0};
  396. const int num_segments = n - 1 - i;
  397. write_ptr = paro_write_geodesic(write_ptr, point_a, point_b, num_segments);
  398. }
  399. int f = 0, j0 = 0;
  400. uint16_t* faces = mesh->indices;
  401. for (int col_index = 0; col_index < n - 1; col_index++) {
  402. const int col_height = n - 1 - col_index;
  403. const int j1 = j0 + 1;
  404. const int j2 = j0 + col_height + 1;
  405. const int j3 = j0 + col_height + 2;
  406. for (int row = 0; row < col_height - 1; row++) {
  407. paro_write_ui3(faces, f++, j0 + row, j1 + row, j2 + row);
  408. paro_write_ui3(faces, f++, j2 + row, j1 + row, j3 + row);
  409. }
  410. const int row = col_height - 1;
  411. paro_write_ui3(faces, f++, j0 + row, j1 + row, j2 + row);
  412. j0 = j2;
  413. }
  414. // END TESSELLATION OF SINGLE PATCH
  415. // START 8-WAY CLONE OF PATCH
  416. // clang-format off
  417. float euler_angles[8][3] = {
  418. {0, 0, 0}, {0, 1, 0}, {0, 2, 0}, {0, 3, 0},
  419. {1, 0, 0}, {1, 0, 1}, {1, 0, 2}, {1, 0, 3},
  420. };
  421. // clang-format on
  422. for (int octant = 1; octant < 8; octant++) {
  423. paro_scale(euler_angles[octant], PARO_PI * 0.5);
  424. float quat[4];
  425. paro_quat_from_eulers(quat, euler_angles[octant]);
  426. float* dst = mesh->positions + octant * verts_per_patch * 3;
  427. const float* src = mesh->positions;
  428. for (int vindex = 0; vindex < verts_per_patch; vindex++, dst += 3, src += 3) {
  429. paro_quat_rotate_vector(dst, quat, src);
  430. }
  431. }
  432. for (int octant = 1; octant < 8; octant++) {
  433. const int indices_per_patch = triangles_per_patch * 3;
  434. uint16_t* dst = mesh->indices + octant * indices_per_patch;
  435. const uint16_t* src = mesh->indices;
  436. const uint16_t offset = verts_per_patch * octant;
  437. for (int iindex = 0; iindex < indices_per_patch; ++iindex) {
  438. dst[iindex] = src[iindex] + offset;
  439. }
  440. }
  441. // END 8-WAY CLONE OF PATCH
  442. if (mesh->texcoords && config->uv_mode == PAR_OCTASPHERE_UV_LATLONG) {
  443. for (int i = 0; i < total_vertices; i++) {
  444. const int octant = i / verts_per_patch;
  445. const int relative_index = i % verts_per_patch;
  446. float* uv = mesh->texcoords + i * 2;
  447. const float* xyz = mesh->positions + i * 3;
  448. const float x = xyz[0], y = xyz[1], z = xyz[2];
  449. const float phi = -atan2(z, x);
  450. const float theta = acos(y);
  451. uv[0] = 0.5 * (phi / PARO_PI + 1.0);
  452. uv[1] = theta / PARO_PI;
  453. // Special case for the north pole.
  454. if (octant < 4 && relative_index == verts_per_patch - 1) {
  455. uv[0] = fmod(0.375 + 0.25 * octant, 1.0);
  456. uv[1] = 0;
  457. }
  458. // Special case for the south pole.
  459. if (octant >= 4 && relative_index == 0) {
  460. uv[0] = 0.375 - 0.25 * (octant - 4);
  461. uv[0] = uv[0] + uv[0] < 0 ? 1.0 : 0.0;
  462. uv[1] = 1.0;
  463. }
  464. // Adjust the prime meridian for proper wrapping.
  465. if ((octant == 2 || octant == 6) && uv[0] < 0.5) {
  466. uv[0] += 1.0;
  467. }
  468. }
  469. }
  470. if (mesh->normals && config->normals_mode == PAR_OCTASPHERE_NORMALS_SMOOTH) {
  471. memcpy(mesh->normals, mesh->positions, sizeof(float) * 3 * total_vertices);
  472. }
  473. if (config->corner_radius != 1.0) {
  474. for (int i = 0; i < total_vertices; i++) {
  475. float* xyz = mesh->positions + i * 3;
  476. xyz[0] *= config->corner_radius;
  477. xyz[1] *= config->corner_radius;
  478. xyz[2] *= config->corner_radius;
  479. }
  480. }
  481. mesh->num_indices = triangles_per_patch * 8 * 3;
  482. mesh->num_vertices = total_vertices;
  483. if (tx == 0 && ty == 0 && tz == 0) {
  484. return;
  485. }
  486. for (int i = 0; i < total_vertices; i++) {
  487. float* xyz = mesh->positions + i * 3;
  488. const int octant = i / verts_per_patch;
  489. const float sx = (octant < 2 || octant == 4 || octant == 7) ? +1 : -1;
  490. const float sy = octant < 4 ? +1 : -1;
  491. const float sz = (octant == 0 || octant == 3 || octant == 4 || octant == 5) ? +1 : -1;
  492. xyz[0] += tx * sx;
  493. xyz[1] += ty * sy;
  494. xyz[2] += tz * sz;
  495. }
  496. paro_add_quads(config, mesh);
  497. }
  498. #endif // PAR_OCTASPHERE_IMPLEMENTATION
  499. #endif // PAR_OCTASPHERE_H
  500. // par_octasphere is distributed under the MIT license:
  501. //
  502. // Copyright (c) 2019 Philip Rideout
  503. //
  504. // Permission is hereby granted, free of charge, to any person obtaining a copy
  505. // of this software and associated documentation files (the "Software"), to deal
  506. // in the Software without restriction, including without limitation the rights
  507. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  508. // copies of the Software, and to permit persons to whom the Software is
  509. // furnished to do so, subject to the following conditions:
  510. //
  511. // The above copyright notice and this permission notice shall be included in
  512. // all copies or substantial portions of the Software.
  513. //
  514. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  515. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  516. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  517. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  518. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  519. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  520. // SOFTWARE.