s3dut_sphere.c (14105B)
1 /* Copyright (C) 2016, 2017, 2020, 2021, 2023 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #include "s3dut.h" 17 #include "s3dut_mesh.h" 18 19 /******************************************************************************* 20 * Helper functions 21 ******************************************************************************/ 22 static res_T 23 setup_sphere_coords 24 (struct mem_allocator* allocator, 25 double** coords_ptr, 26 const double radius, 27 const double z_range[2], 28 const unsigned nslices, /* # subdivisions around the Z axis */ 29 const unsigned nstacks, /* # subdivisions along the Z axis */ 30 const int close_ends) 31 { 32 enum { SIN, COS }; 33 double* coords; 34 struct darray_double sincos_theta; 35 struct darray_double sincos_phi; 36 double step_theta; 37 const int top_truncated = z_range && z_range[1] < +radius; 38 const int bottom_truncated = z_range && z_range[0] > -radius; 39 const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated); 40 const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z); 41 const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z); 42 const unsigned nrings = nstacks - 1 + nb_truncated; 43 const double phi_z_min 44 = bottom_truncated ? asin(CLAMP(z_range[0] / radius, -1, 1)) : -PI / 2; 45 const double phi_z_max 46 = top_truncated ? asin(CLAMP(z_range[1] / radius, -1, 1)) : +PI / 2; 47 double step_phi 48 = (phi_z_max - phi_z_min) / (double)(nrings + 1 - nb_truncated); 49 size_t itheta; 50 size_t iphi; 51 size_t i = 0; 52 res_T res = RES_OK; 53 ASSERT(coords_ptr && *coords_ptr && radius > 0); 54 55 coords = *coords_ptr; 56 darray_double_init(allocator, &sincos_theta); 57 darray_double_init(allocator, &sincos_phi); 58 59 res = darray_double_resize(&sincos_theta, nslices*2/*sin & cos*/); 60 if(res != RES_OK) goto error; 61 res = darray_double_resize(&sincos_phi, nrings*2/*sin & cos*/); 62 if(res != RES_OK) goto error; 63 64 /* Precompute the sinus/cosine of the theta/phi angles */ 65 step_theta = 2*PI / (double)nslices; 66 FOR_EACH(itheta, 0, nslices) { 67 const double theta = -PI + (double)itheta * step_theta; 68 darray_double_data_get(&sincos_theta)[itheta*2 + SIN] = sin(theta); 69 darray_double_data_get(&sincos_theta)[itheta*2 + COS] = cos(theta); 70 } 71 FOR_EACH(iphi, 0, nrings) { 72 const double phi 73 = phi_z_min + (double)(iphi + !bottom_truncated) * step_phi; 74 darray_double_data_get(&sincos_phi)[iphi*2 + SIN] = sin(phi); 75 darray_double_data_get(&sincos_phi)[iphi*2 + COS] = cos(phi); 76 } 77 78 /* Setup the contour vertices */ 79 i = 0; 80 FOR_EACH(itheta, 0, nslices) { 81 const double* theta = darray_double_cdata_get(&sincos_theta) + itheta*2; 82 FOR_EACH(iphi, 0, nrings) { 83 const double* phi = darray_double_cdata_get(&sincos_phi) + iphi*2; 84 coords[i++] = radius * COS[theta] * COS[phi]; 85 coords[i++] = radius * SIN[theta] * COS[phi]; 86 coords[i++] = radius * SIN[phi]; 87 } 88 } 89 90 if(close_bottom || !bottom_truncated) { 91 /* Setup the bottom polar vertex */ 92 coords[i++] = 0; 93 coords[i++] = 0; 94 coords[i++] = bottom_truncated ? z_range[0] : -radius; 95 } 96 97 if(close_top || !top_truncated) { 98 /* Setup the top polar vertex */ 99 coords[i++] = 0; 100 coords[i++] = 0; 101 coords[i++] = top_truncated ? z_range[1] : +radius; 102 } 103 104 exit: 105 darray_double_release(&sincos_theta); 106 darray_double_release(&sincos_phi); 107 *coords_ptr = coords + i; 108 return res; 109 error: 110 goto exit; 111 } 112 113 static size_t* 114 setup_sphere_indices 115 (size_t* ids, 116 const size_t offset, 117 const unsigned nslices, /* # subdivisions around the Z axis */ 118 const unsigned nstacks, /* # subdivisions along the Z axis */ 119 const double radius, 120 const double z_range[2], 121 const int close_ends, 122 const int cw_out) 123 { 124 const int top_truncated = z_range && z_range[1] < +radius; 125 const int bottom_truncated = z_range && z_range[0] > -radius; 126 const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z); 127 const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z); 128 const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated); 129 const unsigned nrings = nstacks - 1 + nb_truncated; 130 size_t ibottom; 131 size_t itop; 132 size_t i, itheta, iphi; 133 ASSERT(ids && nslices && nstacks); 134 135 /* Define the indices of the contour primitives */ 136 i = 0; 137 FOR_EACH(itheta, 0, nslices) { 138 const size_t itheta0 = offset + itheta * nrings; 139 const size_t itheta1 = offset + ((itheta + 1) % nslices) * nrings; 140 FOR_EACH(iphi, 0, nrings-1) { 141 const size_t iphi0 = iphi + 0; 142 const size_t iphi1 = iphi + 1; 143 144 ids[i] = itheta0 + iphi0; 145 ids[cw_out?i+1:i+2] = itheta0 + iphi1; 146 ids[cw_out?i+2:i+1] = itheta1 + iphi0; 147 i += 3; 148 149 ids[i] = itheta1 + iphi0; 150 ids[cw_out?i+1:i+2] = itheta0 + iphi1; 151 ids[cw_out?i+2:i+1] = itheta1 + iphi1; 152 i += 3; 153 } 154 } 155 156 /* Define the indices of the polar primitives */ 157 FOR_EACH(itheta, 0, nslices) { 158 const size_t itheta0 = offset + itheta * nrings; 159 const size_t itheta1 = offset + ((itheta + 1) % nslices) * nrings; 160 161 if(close_bottom || !bottom_truncated) { 162 ibottom = nslices * nrings; 163 ids[i] = offset + ibottom; 164 ids[cw_out?i+1:i+2] = itheta0; 165 ids[cw_out?i+2:i+1] = itheta1; 166 i += 3; 167 } 168 169 if(close_top || !top_truncated) { 170 itop = (close_bottom || !bottom_truncated) 171 ? nslices * nrings + 1 : nslices * nrings; 172 ids[i] = offset + itop; 173 ids[cw_out?i+1:i+2] = itheta1 + (nrings - 1); 174 ids[cw_out?i+2:i+1] = itheta0 + (nrings - 1); 175 i += 3; 176 } 177 } 178 return ids + i; 179 } 180 181 static size_t* 182 close_wall 183 (size_t* ids, 184 const size_t fst_id_out, 185 const size_t fst_id_in, 186 const unsigned nslices, 187 const unsigned external_nrings, 188 const unsigned internal_nrings, 189 const int bottom) 190 { 191 size_t islice; 192 size_t i = 0; 193 ASSERT(ids && nslices >= 3 && external_nrings >= 1 && internal_nrings >= 1); 194 195 FOR_EACH(islice, 0, nslices) { 196 ids[i] = fst_id_out + islice * external_nrings; 197 ids[bottom?i+1:i+2] = fst_id_in + ((islice+1) % nslices) * internal_nrings; 198 ids[bottom?i+2:i+1] = fst_id_in + islice * internal_nrings; 199 i += 3; 200 201 ids[i] = fst_id_out + islice * external_nrings; 202 ids[bottom?i+1:i+2] = fst_id_out + ((islice+1) % nslices) * external_nrings; 203 ids[bottom?i+2:i+1] = fst_id_in + ((islice+1) % nslices) * internal_nrings; 204 i += 3; 205 } 206 return ids + i; 207 } 208 209 static void 210 sphere_accum_counts 211 (const double radius, 212 const unsigned nslices, 213 const unsigned nstacks, 214 const double z_range[2], 215 const int close_ends, 216 unsigned* nrings, 217 size_t* ntris, 218 size_t* nverts) 219 { 220 const int top_truncated = z_range && z_range[1] < +radius; 221 const int bottom_truncated = z_range && z_range[0] > -radius; 222 const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z); 223 const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z); 224 const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated); 225 const unsigned nb_closed_ends = (close_top ? 1u : 0) + (close_bottom ? 1u : 0); 226 const unsigned nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated); 227 ASSERT(nrings && ntris && nverts); 228 *nrings = nstacks - 1 + nb_truncated;; 229 *nverts += nslices*(*nrings) /* #contour verts*/ + nb_pole_vrtx; /*polar verts*/ 230 *ntris += 2 * nslices * (*nrings -1) /* #contour tris */ 231 + (2 - nb_truncated + nb_closed_ends) * nslices; /* #polar tris */ 232 } 233 234 /******************************************************************************* 235 * Exported function 236 ******************************************************************************/ 237 res_T 238 s3dut_create_sphere 239 (struct mem_allocator* allocator, 240 const double radius, 241 const unsigned nslices, 242 const unsigned nstacks, 243 struct s3dut_mesh** mesh) 244 { 245 return s3dut_create_truncated_sphere 246 (allocator, radius, nslices, nstacks, NULL, 0, mesh); 247 } 248 249 res_T 250 s3dut_create_hemisphere 251 (struct mem_allocator* allocator, 252 const double radius, 253 const unsigned nslices, 254 const unsigned nstacks, 255 struct s3dut_mesh** mesh) 256 { 257 double z_range[2]; 258 z_range[0] = 0; 259 z_range[1] = +radius; 260 return s3dut_create_truncated_sphere 261 (allocator, radius, nslices, nstacks, z_range, 0, mesh); 262 } 263 264 res_T 265 s3dut_create_truncated_sphere 266 (struct mem_allocator* allocator, 267 const double radius, 268 const unsigned nslices, 269 const unsigned nstacks, 270 const double z_range[2], 271 const int close_ends, 272 struct s3dut_mesh** mesh) 273 { 274 struct s3dut_mesh* sphere = NULL; 275 const int top_truncated = z_range && z_range[1] < +radius; 276 const int bottom_truncated = z_range && z_range[0] > -radius; 277 const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z); 278 const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z); 279 const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated); 280 const unsigned nb_closed_ends = (close_top ? 1u : 0) + (close_bottom ? 1u : 0); 281 const unsigned nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated); 282 const unsigned nrings = nstacks - 1 + nb_truncated; 283 const size_t nverts = nslices*nrings/* #contour verts*/ 284 + nb_pole_vrtx/*polar verts*/; 285 const size_t ntris = 2 * nslices*(nrings - 1)/* #contour tris*/ 286 + nb_pole_vrtx*nslices/* #polar tris*/; 287 double* coords; 288 res_T res = RES_OK; 289 ASSERT(nb_truncated <= 2); 290 291 if(radius <= 0 || nslices < 3 || nstacks < 2 || !mesh 292 || (z_range && z_range[0] >= z_range[1])) { 293 res = RES_BAD_ARG; 294 goto error; 295 } 296 297 res = mesh_create(allocator, S3DUT_MESH_SPHERE, nverts, ntris, &sphere); 298 if(res != RES_OK) goto error; 299 300 coords = darray_double_data_get(&sphere->coords); 301 res = setup_sphere_coords(allocator, &coords, radius, z_range, nslices, 302 nstacks, close_ends); 303 if(res != RES_OK) goto error; 304 305 setup_sphere_indices(darray_size_t_data_get(&sphere->ids), 0, 306 nslices, nstacks, radius, z_range, close_ends, 1); 307 308 exit: 309 if(mesh) *mesh = sphere; 310 return res; 311 error: 312 if(sphere) { 313 S3DUT(mesh_ref_put(sphere)); 314 sphere = NULL; 315 } 316 goto exit; 317 } 318 319 res_T 320 s3dut_create_thick_truncated_sphere 321 (struct mem_allocator* allocator, 322 const double radius, 323 const double thickness, 324 const unsigned nslices, 325 const unsigned nstacks, 326 const double z_range[2], 327 const int c_e, 328 struct s3dut_mesh** mesh) 329 { 330 struct s3dut_mesh* sphere = NULL; 331 int close_ends = c_e; 332 double* coords = NULL; 333 double* prev_coords; 334 size_t* ids_out = NULL; 335 size_t* ids_in = NULL; 336 size_t* ids_walls = NULL; 337 size_t id_offset; 338 const double internal_radius = radius - thickness; 339 const int top_truncated = z_range && (z_range[1] < +radius); 340 const int bottom_truncated = z_range && (z_range[0] > -radius); 341 const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z); 342 const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z); 343 const int top_seam = z_range && (z_range[1] < internal_radius) && !close_top; 344 const int bottom_seam 345 = z_range && (z_range[0] > -internal_radius) && ! close_bottom; 346 unsigned external_nrings; 347 unsigned internal_nrings; 348 const unsigned nb_seams = (unsigned)(top_seam + bottom_seam); 349 size_t nverts = 0; 350 size_t ntris = 0; 351 double z_internal_range[2]; 352 res_T res = RES_OK; 353 354 if(radius <= thickness || thickness <= 0 || nslices < 3 || nstacks < 2 355 || !mesh || (z_range && z_range[0] >= z_range[1])) { 356 return RES_BAD_ARG; 357 } 358 359 /* Special case when a single sphere is truncated */ 360 if(top_truncated && (z_range[1] >= internal_radius)) { 361 close_ends |= S3DUT_CAP_POS_Z; /* close the external sphere's top end */ 362 } 363 if(bottom_truncated && (z_range[0] <= -internal_radius)) { 364 close_ends |= S3DUT_CAP_NEG_Z; /* close the external sphere's bottom end */ 365 } 366 z_internal_range[0] = (bottom_truncated && !close_bottom) 367 ? z_range[0] : (z_range ? z_range[0] : -radius) + thickness; 368 z_internal_range[1] = (top_truncated && !close_top) ? 369 z_range[1] : (z_range ? z_range[1] : +radius) - thickness; 370 sphere_accum_counts(radius, nslices, nstacks, z_range, close_ends, 371 &external_nrings, &ntris, &nverts); 372 sphere_accum_counts(radius-thickness, nslices, nstacks, z_internal_range, 373 close_ends, &internal_nrings, &ntris, &nverts); 374 ntris += 2 * nb_seams * nslices; /* # seam tris */ 375 res = mesh_create(allocator, S3DUT_MESH_THICK_SPHERE, nverts, ntris, &sphere); 376 if(res != RES_OK) goto error; 377 378 coords = darray_double_data_get(&sphere->coords); 379 ids_out = darray_size_t_data_get(&sphere->ids); 380 /* External sphere */ 381 prev_coords = coords; 382 setup_sphere_coords 383 (allocator, &coords, radius, z_range, nslices, nstacks, close_ends); 384 ids_in = setup_sphere_indices(ids_out, 0, nslices, nstacks, radius, z_range, 385 close_ends, 1); 386 /* Internal sphere */ 387 id_offset = (size_t)(coords - prev_coords); 388 ASSERT(id_offset % 3 == 0); 389 id_offset /= 3; 390 setup_sphere_coords(allocator, &coords, radius - thickness, z_internal_range, 391 nslices, nstacks, close_ends); 392 ids_walls = setup_sphere_indices (ids_in, id_offset, nslices, 393 nstacks, radius - thickness, z_internal_range, close_ends, 0); 394 if(bottom_seam) { 395 ids_walls = close_wall(ids_walls, 0, id_offset, nslices, external_nrings, 396 internal_nrings, 1); 397 } 398 if(top_seam) { 399 ids_walls = close_wall(ids_walls, nstacks, id_offset + nstacks, nslices, 400 external_nrings, internal_nrings, 0); 401 } 402 403 exit: 404 if(mesh) *mesh = sphere; 405 return res; 406 error: 407 if(sphere) { 408 S3DUT(mesh_ref_put(sphere)); 409 sphere = NULL; 410 } 411 goto exit; 412 }