par_octasphere.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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. for (int point_index = 1; point_index < num_segments; point_index++, dst += 3) {
  196. paro_quat_from_rotation(quat, rotation_axis, dtheta * point_index);
  197. paro_quat_rotate_vector(dst, quat, point_a);
  198. }
  199. return paro_write_f3(dst, point_b);
  200. }
  201. void paro_add_quads(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  202. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  203. const int n = (1 << ndivisions) + 1;
  204. const int verts_per_patch = n * (n + 1) / 2;
  205. const float r2 = config->corner_radius * 2;
  206. const float w = PARO_MAX(config->width, r2);
  207. const float h = PARO_MAX(config->height, r2);
  208. const float d = PARO_MAX(config->depth, r2);
  209. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  210. // Find the vertex indices along each of the patch's 3 edges.
  211. uint16_t boundaries[3][PARO_MAX_BOUNDARY_LENGTH];
  212. int a = 0, b = 0, c = 0, row;
  213. uint16_t j0 = 0;
  214. for (int col_index = 0; col_index < n - 1; col_index++) {
  215. int col_height = n - 1 - col_index;
  216. uint16_t j1 = j0 + 1;
  217. boundaries[0][a++] = j0;
  218. for (row = 0; row < col_height - 1; row++) {
  219. if (col_height == n - 1) {
  220. boundaries[2][c++] = j0 + row;
  221. }
  222. }
  223. if (col_height == n - 1) {
  224. boundaries[2][c++] = j0 + row;
  225. boundaries[2][c++] = j1 + row;
  226. }
  227. boundaries[1][b++] = j1 + row;
  228. j0 += col_height + 1;
  229. }
  230. boundaries[0][a] = boundaries[1][b] = j0 + row;
  231. // If there is no rounding (i.e. this is a plain box), then clobber the existing indices.
  232. if (!PARO_CONSTANT_TOPOLOGY && config->corner_radius == 0) {
  233. mesh->num_indices = 0;
  234. }
  235. uint16_t* write_ptr = mesh->indices + mesh->num_indices;
  236. const uint16_t* begin_ptr = write_ptr;
  237. if (PARO_CONSTANT_TOPOLOGY || config->corner_radius > 0) {
  238. // Go around the top half.
  239. for (int patch = 0; patch < 4; patch++) {
  240. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 0 && tz == 0) continue;
  241. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 1 && tx == 0) continue;
  242. const int next_patch = (patch + 1) % 4;
  243. const uint16_t* boundary_a = boundaries[1];
  244. const uint16_t* boundary_b = boundaries[0];
  245. const uint16_t offset_a = verts_per_patch * patch;
  246. const uint16_t offset_b = verts_per_patch * next_patch;
  247. for (int i = 0; i < n - 1; i++) {
  248. const uint16_t a = boundary_a[i] + offset_a;
  249. const uint16_t b = boundary_b[i] + offset_b;
  250. const uint16_t c = boundary_a[i + 1] + offset_a;
  251. const uint16_t d = boundary_b[i + 1] + offset_b;
  252. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  253. }
  254. }
  255. // Go around the bottom half.
  256. for (int patch = 4; patch < 8; patch++) {
  257. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 0 && tx == 0) continue;
  258. if (!PARO_CONSTANT_TOPOLOGY && (patch % 2) == 1 && tz == 0) continue;
  259. const int next_patch = 4 + (patch + 1) % 4;
  260. const uint16_t* boundary_a = boundaries[0];
  261. const uint16_t* boundary_b = boundaries[2];
  262. const uint16_t offset_a = verts_per_patch * patch;
  263. const uint16_t offset_b = verts_per_patch * next_patch;
  264. for (int i = 0; i < n - 1; i++) {
  265. const uint16_t a = boundary_a[i] + offset_a;
  266. const uint16_t b = boundary_b[i] + offset_b;
  267. const uint16_t c = boundary_a[i + 1] + offset_a;
  268. const uint16_t d = boundary_b[i + 1] + offset_b;
  269. write_ptr = paro_write_quad(write_ptr, d, b, a, c);
  270. }
  271. }
  272. // Connect the top and bottom halves.
  273. if (PARO_CONSTANT_TOPOLOGY || ty > 0) {
  274. for (int patch = 0; patch < 4; patch++) {
  275. const int next_patch = 4 + (4 - patch) % 4;
  276. const uint16_t* boundary_a = boundaries[2];
  277. const uint16_t* boundary_b = boundaries[1];
  278. const uint16_t offset_a = verts_per_patch * patch;
  279. const uint16_t offset_b = verts_per_patch * next_patch;
  280. for (int i = 0; i < n - 1; i++) {
  281. const uint16_t a = boundary_a[i] + offset_a;
  282. const uint16_t b = boundary_b[n - 1 - i] + offset_b;
  283. const uint16_t c = boundary_a[i + 1] + offset_a;
  284. const uint16_t d = boundary_b[n - 1 - i - 1] + offset_b;
  285. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  286. }
  287. }
  288. }
  289. }
  290. // Fill in the top and bottom holes.
  291. if (PARO_CONSTANT_TOPOLOGY || tx > 0 || ty > 0) {
  292. uint16_t a, b, c, d;
  293. a = boundaries[0][n - 1];
  294. b = a + verts_per_patch;
  295. c = b + verts_per_patch;
  296. d = c + verts_per_patch;
  297. write_ptr = paro_write_quad(write_ptr, a, b, c, d);
  298. a = boundaries[2][0] + verts_per_patch * 4;
  299. b = a + verts_per_patch;
  300. c = b + verts_per_patch;
  301. d = c + verts_per_patch;
  302. write_ptr = paro_write_quad(write_ptr, a, b, c, d);
  303. }
  304. // Fill in the side holes.
  305. if (PARO_CONSTANT_TOPOLOGY || ty > 0) {
  306. const int sides[4][2] = {{7, 0}, {1, 2}, {3, 4}, {5, 6}};
  307. for (int side = 0; side < 4; side++) {
  308. int patch_index, patch, next_patch;
  309. uint16_t *boundary_a, *boundary_b;
  310. uint16_t offset_a, offset_b;
  311. uint16_t a, b;
  312. patch_index = sides[side][0];
  313. patch = patch_index / 2;
  314. next_patch = 4 + (4 - patch) % 4;
  315. offset_a = verts_per_patch * patch;
  316. offset_b = verts_per_patch * next_patch;
  317. boundary_a = boundaries[2];
  318. boundary_b = boundaries[1];
  319. if (patch_index % 2 == 0) {
  320. a = boundary_a[0] + offset_a;
  321. b = boundary_b[n - 1] + offset_b;
  322. } else {
  323. a = boundary_a[n - 1] + offset_a;
  324. b = boundary_b[0] + offset_b;
  325. }
  326. uint16_t c, d;
  327. patch_index = sides[side][1];
  328. patch = patch_index / 2;
  329. next_patch = 4 + (4 - patch) % 4;
  330. offset_a = verts_per_patch * patch;
  331. offset_b = verts_per_patch * next_patch;
  332. boundary_a = boundaries[2];
  333. boundary_b = boundaries[1];
  334. if (patch_index % 2 == 0) {
  335. c = boundary_a[0] + offset_a;
  336. d = boundary_b[n - 1] + offset_b;
  337. } else {
  338. c = boundary_a[n - 1] + offset_a;
  339. d = boundary_b[0] + offset_b;
  340. }
  341. write_ptr = paro_write_quad(write_ptr, a, b, d, c);
  342. }
  343. }
  344. mesh->num_indices += write_ptr - begin_ptr;
  345. #ifndef NDEBUG
  346. uint32_t expected_indices;
  347. uint32_t expected_vertices;
  348. par_octasphere_get_counts(config, &expected_indices, &expected_vertices);
  349. assert(mesh->num_indices <= expected_indices);
  350. #endif
  351. }
  352. void par_octasphere_get_counts(const par_octasphere_config* config, uint32_t* num_indices,
  353. uint32_t* num_vertices) {
  354. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  355. const int n = (1 << ndivisions) + 1;
  356. const int verts_per_patch = n * (n + 1) / 2;
  357. const float r2 = config->corner_radius * 2;
  358. const float w = PARO_MAX(config->width, r2);
  359. const float h = PARO_MAX(config->height, r2);
  360. const float d = PARO_MAX(config->depth, r2);
  361. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  362. const int triangles_per_patch = (n - 2) * (n - 1) + n - 1;
  363. // If this is a sphere, return early.
  364. if (tx == 0 && ty == 0 && tz == 0) {
  365. *num_indices = triangles_per_patch * 8 * 3;
  366. *num_vertices = verts_per_patch * 8;
  367. return;
  368. }
  369. // This is a cuboid, so account for the maximum number of possible quads.
  370. // - 4*(n-1) quads between the 4 top patches.
  371. // - 4*(n-1) quads between the 4 bottom patches.
  372. // - 4*(n-1) quads between the top and bottom patches.
  373. // - 6 quads to fill "holes" in each cuboid face.
  374. const int num_connection_quads = (4 + 4 + 4) * (n - 1) + 6;
  375. *num_indices = (triangles_per_patch * 8 + num_connection_quads * 2) * 3;
  376. *num_vertices = verts_per_patch * 8;
  377. }
  378. void par_octasphere_populate(const par_octasphere_config* config, par_octasphere_mesh* mesh) {
  379. const int ndivisions = PARO_CLAMP(config->num_subdivisions, 0, PAR_OCTASPHERE_MAX_SUBDIVISIONS);
  380. const int n = (1 << ndivisions) + 1;
  381. const int verts_per_patch = n * (n + 1) / 2;
  382. const float r2 = config->corner_radius * 2;
  383. const float w = PARO_MAX(config->width, r2);
  384. const float h = PARO_MAX(config->height, r2);
  385. const float d = PARO_MAX(config->depth, r2);
  386. const float tx = (w - r2) / 2, ty = (h - r2) / 2, tz = (d - r2) / 2;
  387. const int triangles_per_patch = (n - 2) * (n - 1) + n - 1;
  388. const int total_vertices = verts_per_patch * 8;
  389. // START TESSELLATION OF SINGLE PATCH (one-eighth of the octasphere)
  390. float* write_ptr = mesh->positions;
  391. for (int i = 0; i < n; i++) {
  392. const float theta = PARO_PI * 0.5 * i / (n - 1);
  393. const float point_a[] = {0, sin(theta), cos(theta)};
  394. const float point_b[] = {cos(theta), sin(theta), 0};
  395. const int num_segments = n - 1 - i;
  396. write_ptr = paro_write_geodesic(write_ptr, point_a, point_b, num_segments);
  397. }
  398. int f = 0, j0 = 0;
  399. uint16_t* faces = mesh->indices;
  400. for (int col_index = 0; col_index < n - 1; col_index++) {
  401. const int col_height = n - 1 - col_index;
  402. const int j1 = j0 + 1;
  403. const int j2 = j0 + col_height + 1;
  404. const int j3 = j0 + col_height + 2;
  405. for (int row = 0; row < col_height - 1; row++) {
  406. paro_write_ui3(faces, f++, j0 + row, j1 + row, j2 + row);
  407. paro_write_ui3(faces, f++, j2 + row, j1 + row, j3 + row);
  408. }
  409. const int row = col_height - 1;
  410. paro_write_ui3(faces, f++, j0 + row, j1 + row, j2 + row);
  411. j0 = j2;
  412. }
  413. // END TESSELLATION OF SINGLE PATCH
  414. // START 8-WAY CLONE OF PATCH
  415. // clang-format off
  416. float euler_angles[8][3] = {
  417. {0, 0, 0}, {0, 1, 0}, {0, 2, 0}, {0, 3, 0},
  418. {1, 0, 0}, {1, 0, 1}, {1, 0, 2}, {1, 0, 3},
  419. };
  420. // clang-format on
  421. for (int octant = 1; octant < 8; octant++) {
  422. paro_scale(euler_angles[octant], PARO_PI * 0.5);
  423. float quat[4];
  424. paro_quat_from_eulers(quat, euler_angles[octant]);
  425. float* dst = mesh->positions + octant * verts_per_patch * 3;
  426. const float* src = mesh->positions;
  427. for (int vindex = 0; vindex < verts_per_patch; vindex++, dst += 3, src += 3) {
  428. paro_quat_rotate_vector(dst, quat, src);
  429. }
  430. }
  431. for (int octant = 1; octant < 8; octant++) {
  432. const int indices_per_patch = triangles_per_patch * 3;
  433. uint16_t* dst = mesh->indices + octant * indices_per_patch;
  434. const uint16_t* src = mesh->indices;
  435. const uint16_t offset = verts_per_patch * octant;
  436. for (int iindex = 0; iindex < indices_per_patch; ++iindex) {
  437. dst[iindex] = src[iindex] + offset;
  438. }
  439. }
  440. // END 8-WAY CLONE OF PATCH
  441. if (mesh->texcoords && config->uv_mode == PAR_OCTASPHERE_UV_LATLONG) {
  442. for (int i = 0; i < total_vertices; i++) {
  443. const int octant = i / verts_per_patch;
  444. const int relative_index = i % verts_per_patch;
  445. float* uv = mesh->texcoords + i * 2;
  446. const float* xyz = mesh->positions + i * 3;
  447. const float x = xyz[0], y = xyz[1], z = xyz[2];
  448. const float phi = -atan2(z, x);
  449. const float theta = acos(y);
  450. uv[0] = 0.5 * (phi / PARO_PI + 1.0);
  451. uv[1] = theta / PARO_PI;
  452. // Special case for the north pole.
  453. if (octant < 4 && relative_index == verts_per_patch - 1) {
  454. uv[0] = fmod(0.375 + 0.25 * octant, 1.0);
  455. uv[1] = 0;
  456. }
  457. // Special case for the south pole.
  458. if (octant >= 4 && relative_index == 0) {
  459. uv[0] = 0.375 - 0.25 * (octant - 4);
  460. uv[0] = uv[0] + uv[0] < 0 ? 1.0 : 0.0;
  461. uv[1] = 1.0;
  462. }
  463. // Adjust the prime meridian for proper wrapping.
  464. if ((octant == 2 || octant == 6) && uv[0] < 0.5) {
  465. uv[0] += 1.0;
  466. }
  467. }
  468. }
  469. if (mesh->normals && config->normals_mode == PAR_OCTASPHERE_NORMALS_SMOOTH) {
  470. memcpy(mesh->normals, mesh->positions, sizeof(float) * 3 * total_vertices);
  471. }
  472. if (config->corner_radius != 1.0) {
  473. for (int i = 0; i < total_vertices; i++) {
  474. float* xyz = mesh->positions + i * 3;
  475. xyz[0] *= config->corner_radius;
  476. xyz[1] *= config->corner_radius;
  477. xyz[2] *= config->corner_radius;
  478. }
  479. }
  480. mesh->num_indices = triangles_per_patch * 8 * 3;
  481. mesh->num_vertices = total_vertices;
  482. if (tx == 0 && ty == 0 && tz == 0) {
  483. return;
  484. }
  485. for (int i = 0; i < total_vertices; i++) {
  486. float* xyz = mesh->positions + i * 3;
  487. const int octant = i / verts_per_patch;
  488. const float sx = (octant < 2 || octant == 4 || octant == 7) ? +1 : -1;
  489. const float sy = octant < 4 ? +1 : -1;
  490. const float sz = (octant == 0 || octant == 3 || octant == 4 || octant == 5) ? +1 : -1;
  491. xyz[0] += tx * sx;
  492. xyz[1] += ty * sy;
  493. xyz[2] += tz * sz;
  494. }
  495. paro_add_quads(config, mesh);
  496. }
  497. #endif // PAR_OCTASPHERE_IMPLEMENTATION
  498. #endif // PAR_OCTASPHERE_H
  499. // par_octasphere is distributed under the MIT license:
  500. //
  501. // Copyright (c) 2019 Philip Rideout
  502. //
  503. // Permission is hereby granted, free of charge, to any person obtaining a copy
  504. // of this software and associated documentation files (the "Software"), to deal
  505. // in the Software without restriction, including without limitation the rights
  506. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  507. // copies of the Software, and to permit persons to whom the Software is
  508. // furnished to do so, subject to the following conditions:
  509. //
  510. // The above copyright notice and this permission notice shall be included in
  511. // all copies or substantial portions of the Software.
  512. //
  513. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  514. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  515. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  516. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  517. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  518. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  519. // SOFTWARE.