s3d_mesh.c (14846B)
1 /* Copyright (C) 2015-2023, 2026 |Méso|Star> (contact@meso-star.com) 2 * 3 * This file is part of Star-3D. 4 * 5 * Star-3D is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * Star-3D is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with Star-3D. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "s3d_c.h" 19 #include "s3d_device_c.h" 20 #include "s3d_mesh.h" 21 22 #include <rsys/float3.h> 23 24 /* Number of floats added to the vertex position in order to ensure the Embree 25 * vertex padding constraint */ 26 #define POSITION_PADDING 1 27 28 /******************************************************************************* 29 * Helper functions 30 ******************************************************************************/ 31 static void 32 mesh_setup_indices 33 (struct mesh* mesh, 34 const unsigned ntris, 35 void (*get_indices)(const unsigned itri, unsigned ids[3], void*), 36 const unsigned nverts, 37 void* data) 38 { 39 uint32_t* indices; 40 unsigned itri; 41 unsigned ntris_prev; 42 unsigned nverts_new; 43 res_T res; 44 ASSERT(mesh && ntris && nverts); 45 46 ntris_prev = (unsigned)mesh_get_ntris(mesh); 47 ASSERT(get_indices != S3D_KEEP || ntris == ntris_prev); 48 (void)ntris_prev; 49 50 if(get_indices == S3D_KEEP) 51 return; 52 53 if(mesh->indices) { /* Release the old index buffer */ 54 index_buffer_ref_put(mesh->indices); 55 mesh->indices = NULL; 56 } 57 58 /* Allocate the new index buffer */ 59 res = index_buffer_create(mesh->dev->allocator, &mesh->indices); 60 if(res != RES_OK) FATAL("Unsufficient memory\n"); 61 res = darray_u32_resize(&mesh->indices->data, ntris * 3/*# triangle ids*/); 62 if(res != RES_OK) FATAL("Unsufficient memory\n"); 63 64 /* Setup the mesh indices */ 65 indices = mesh_get_ids(mesh); 66 nverts_new = 0; 67 FOR_EACH(itri, 0, ntris) { 68 uint32_t* ids = indices + itri*3; 69 int i; 70 STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type); 71 get_indices(itri, ids, data); 72 FOR_EACH(i, 0, 3) nverts_new = MMAX(nverts_new, ids[i]); 73 } 74 /* Transform nverts from the last vertex id to vertices count */ 75 ++nverts_new; 76 if(nverts_new > nverts) 77 FATAL("Out of bound indexation\n"); 78 } 79 80 static void 81 mesh_setup_positions 82 (struct mesh* mesh, 83 const unsigned nverts, 84 struct s3d_vertex_data* attr, 85 void* data) 86 { 87 float* positions; 88 unsigned ivert; 89 res_T res; 90 ASSERT(mesh && nverts && attr && attr->usage == S3D_POSITION); 91 92 if(attr->get == S3D_KEEP) { 93 ASSERT(mesh->attribs[S3D_POSITION]); 94 ASSERT(darray_float_size_get 95 (&mesh->attribs[S3D_POSITION]->data) - POSITION_PADDING == nverts*3); 96 return; 97 } 98 99 if(mesh->attribs[S3D_POSITION]) { /* Release the old vertex buffer */ 100 vertex_buffer_ref_put(mesh->attribs[S3D_POSITION]); 101 mesh->attribs[S3D_POSITION] = NULL; 102 } 103 104 /* Allocate vertex positions */ 105 res = vertex_buffer_create(mesh->dev->allocator, &mesh->attribs[S3D_POSITION]); 106 if(res != RES_OK) FATAL("Insufficient memory\n"); 107 108 /* Embree requires that the last element is at least 16bytes length. One has 109 * thus to add some padding to the buffer of positions. */ 110 res = darray_float_resize 111 (&mesh->attribs[S3D_POSITION]->data, nverts*3 + POSITION_PADDING); 112 if(res != RES_OK) FATAL("Insufficient memory\n"); 113 mesh->attribs_type[S3D_POSITION] = S3D_FLOAT3; 114 115 /* Setup the vertex positions */ 116 positions = darray_float_data_get(&mesh->attribs[S3D_POSITION]->data); 117 if(attr->type == S3D_FLOAT3) { 118 FOR_EACH(ivert, 0, nverts) { 119 attr->get(ivert, positions + ivert*3, data); 120 } 121 } else { 122 FOR_EACH(ivert, 0, nverts) { 123 float pos[4]; 124 unsigned ipos = ivert * 3; 125 attr->get(ivert, pos, data); 126 switch(attr->type) { 127 case S3D_FLOAT: 128 positions[ipos + 0] = pos[0]; 129 positions[ipos + 1] = 0.f; 130 positions[ipos + 2] = 0.f; 131 break; 132 case S3D_FLOAT2: 133 positions[ipos + 0] = pos[0]; 134 positions[ipos + 1] = pos[1]; 135 positions[ipos + 2] = 0.f; 136 break; 137 case S3D_FLOAT4: /* Homogeneous coordinates */ 138 positions[ipos + 0] = pos[0] / pos[3]; 139 positions[ipos + 1] = pos[1] / pos[3]; 140 positions[ipos + 2] = pos[2] / pos[3]; 141 break; 142 default: FATAL("Unreachable code\n"); break; 143 } 144 } 145 } 146 } 147 148 static void 149 mesh_setup_attribs 150 (struct mesh* mesh, 151 const unsigned nverts, 152 const struct s3d_vertex_data* attr, 153 void* data) 154 { 155 float* attr_data; 156 unsigned dim; 157 unsigned ivert; 158 res_T res; 159 ASSERT(mesh && nverts && attr); 160 ASSERT(attr->usage >= S3D_ATTRIB_0 && attr->usage < S3D_ATTRIBS_COUNT__); 161 162 dim = s3d_type_get_dimension(attr->type); 163 if(attr->get == S3D_KEEP) { 164 ASSERT(mesh->attribs_type[attr->usage] == attr->type); 165 ASSERT(mesh->attribs[attr->usage]); 166 ASSERT(darray_float_size_get(&mesh->attribs[attr->usage]->data) == nverts*dim); 167 return; 168 } 169 170 if(mesh->attribs[attr->usage]) { /* Release the previous vertex buffer */ 171 vertex_buffer_ref_put(mesh->attribs[attr->usage]); 172 mesh->attribs[attr->usage] = NULL; 173 } 174 175 /* Allocate the new vertex buffer */ 176 res = vertex_buffer_create(mesh->dev->allocator, &mesh->attribs[attr->usage]); 177 if(res != RES_OK) FATAL("Insufficient memory\n"); 178 res = darray_float_resize(&mesh->attribs[attr->usage]->data, nverts * dim); 179 if(res != RES_OK) FATAL("Insufficient memory\n"); 180 mesh->attribs_type[attr->usage] = attr->type; 181 182 /* Setup the vertex attrib */ 183 attr_data = darray_float_data_get(&mesh->attribs[attr->usage]->data); 184 FOR_EACH(ivert, 0, nverts) { 185 attr->get(ivert, attr_data, data); 186 attr_data += dim; 187 } 188 } 189 190 static FINLINE float 191 mesh_compute_triangle_2area(struct mesh* mesh, const size_t itri) 192 { 193 const uint32_t* ids; 194 const float* pos; 195 const float* v0, *v1, *v2; 196 const size_t id = itri * 3/*#ids per faces*/; 197 float E0[3], E1[3], N[3]; 198 ASSERT(mesh && itri < mesh_get_ntris(mesh)); 199 200 ids = mesh_get_ids(mesh); 201 pos = mesh_get_pos(mesh); 202 203 v0 = pos + ids[id+0]*3/*#coords*/; 204 v1 = pos + ids[id+1]*3/*#coords*/; 205 v2 = pos + ids[id+2]*3/*#coords*/; 206 f3_sub(E0, v1, v0); 207 f3_sub(E1, v2, v0); 208 209 return f3_len(f3_cross(N, E0, E1)); 210 } 211 212 static void 213 mesh_release(ref_T* ref) 214 { 215 struct mesh* msh; 216 struct s3d_device* dev; 217 ASSERT(ref); 218 219 msh = CONTAINER_OF(ref, struct mesh, ref); 220 mesh_clear(msh); 221 dev = msh->dev; 222 darray_float_release(&msh->cdf); 223 MEM_RM(dev->allocator, msh); 224 S3D(device_ref_put(dev)); 225 } 226 227 /******************************************************************************* 228 * Local functions 229 ******************************************************************************/ 230 res_T 231 mesh_create(struct s3d_device* dev, struct mesh** out_mesh) 232 { 233 struct mesh* mesh = NULL; 234 res_T res = RES_OK; 235 ASSERT(dev && out_mesh); 236 237 mesh = (struct mesh*)MEM_CALLOC(dev->allocator, 1, sizeof(struct mesh)); 238 if(!mesh) { 239 res = RES_MEM_ERR; 240 goto error; 241 } 242 ref_init(&mesh->ref); 243 S3D(device_ref_get(dev)); 244 mesh->dev = dev; 245 darray_float_init(dev->allocator, &mesh->cdf); 246 247 exit: 248 *out_mesh = mesh; 249 return res; 250 error: 251 if(mesh) { 252 mesh_ref_put(mesh); 253 mesh = NULL; 254 } 255 goto exit; 256 } 257 258 void 259 mesh_ref_get(struct mesh* mesh) 260 { 261 ASSERT(mesh); 262 ref_get(&mesh->ref); 263 } 264 265 void 266 mesh_ref_put(struct mesh* mesh) 267 { 268 ASSERT(mesh); 269 ref_put(&mesh->ref, mesh_release); 270 } 271 272 void 273 mesh_clear(struct mesh* mesh) 274 { 275 size_t iattr; 276 ASSERT(mesh); 277 if(mesh->indices) { 278 index_buffer_ref_put(mesh->indices); 279 mesh->indices = NULL; 280 } 281 FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) { 282 if(mesh->attribs[iattr]) { 283 vertex_buffer_ref_put(mesh->attribs[iattr]); 284 mesh->attribs[iattr] = NULL; 285 } 286 } 287 darray_float_clear(&mesh->cdf); 288 } 289 290 size_t 291 mesh_get_ntris(const struct mesh* mesh) 292 { 293 size_t nids; 294 ASSERT(mesh); 295 if(!mesh->indices) 296 return 0; 297 nids = darray_u32_size_get(&mesh->indices->data); 298 ASSERT(nids % 3 == 0); /* Only triangular meshes are supported */ 299 return nids / 3; 300 } 301 302 size_t 303 mesh_get_nverts(const struct mesh* mesh) 304 { 305 size_t ncoords; 306 ASSERT(mesh); 307 if(!mesh->attribs[S3D_POSITION]) 308 return 0; 309 310 ASSERT(mesh->attribs_type[S3D_POSITION] == S3D_FLOAT3); 311 ncoords = darray_float_size_get 312 (&mesh->attribs[S3D_POSITION]->data) - POSITION_PADDING; 313 ASSERT(ncoords % 3 == 0); 314 return ncoords / 3; 315 } 316 317 uint32_t* 318 mesh_get_ids(struct mesh* mesh) 319 { 320 ASSERT(mesh && mesh->indices); 321 return darray_u32_data_get(&mesh->indices->data); 322 } 323 324 float* 325 mesh_get_pos(struct mesh* mesh) 326 { 327 ASSERT(mesh && mesh->attribs[S3D_POSITION]); 328 ASSERT(mesh->attribs_type[S3D_POSITION] == S3D_FLOAT3); 329 return darray_float_data_get(&mesh->attribs[S3D_POSITION]->data); 330 } 331 332 float* 333 mesh_get_attr(struct mesh* mesh, const enum s3d_attrib_usage usage) 334 { 335 ASSERT(mesh && usage < S3D_ATTRIBS_COUNT__ && mesh->attribs[usage]); 336 return darray_float_data_get(&mesh->attribs[usage]->data); 337 } 338 339 float 340 mesh_compute_area(struct mesh* mesh) 341 { 342 size_t itri, ntris; 343 float area = 0.f; 344 ASSERT(mesh); 345 346 ntris = mesh_get_ntris(mesh); 347 if(!ntris) return 0.f; 348 349 FOR_EACH(itri, 0, ntris) 350 area += mesh_compute_triangle_2area(mesh, itri); 351 return area * 0.5f; 352 } 353 354 res_T 355 mesh_compute_cdf(struct mesh* mesh) 356 { 357 size_t itri, ntris; 358 float area = 0.f; 359 res_T res = RES_OK; 360 ASSERT(mesh); 361 362 darray_float_clear(&mesh->cdf); 363 364 ntris = mesh_get_ntris(mesh); 365 if(!ntris) goto exit; 366 367 res = darray_float_resize(&mesh->cdf, ntris); 368 if(res != RES_OK) goto error; 369 370 FOR_EACH(itri, 0, ntris) { 371 area += mesh_compute_triangle_2area(mesh, itri) * 0.5f; 372 darray_float_data_get(&mesh->cdf)[itri] = area; 373 } 374 exit: 375 return res; 376 error: 377 darray_float_clear(&mesh->cdf); 378 goto exit; 379 } 380 381 float 382 mesh_compute_volume(struct mesh* mesh, const char flip_surface) 383 { 384 const uint32_t* ids; 385 const float* pos; 386 size_t itri, ntris; 387 double volume = 0.0; 388 ASSERT(mesh); 389 390 ntris = mesh_get_ntris(mesh); 391 if(!ntris) return 0.f; 392 393 ids = mesh_get_ids(mesh); 394 pos = mesh_get_pos(mesh); 395 396 /* Build a tetrahedron whose base is the triangle and whose apex is the 397 * coordinate system's origin. Then compute the volume of the tetrahedron and 398 * add or sub it from the overall volume whether the normal point toward or 399 * backward the apex */ 400 FOR_EACH(itri, 0, ntris) { 401 float E0[3], E1[3], N[3]; 402 float B, h; 403 const size_t id = itri * 3/*#ids per faces*/; 404 const float* v0 = pos + ids[id+0]*3/*#coords*/; 405 const float* v1 = pos + ids[id+1]*3/*#coords*/; 406 const float* v2 = pos + ids[id+2]*3/*#coords*/; 407 /* Front face is CW by default */ 408 f3_sub(E0, v2, v0); 409 f3_sub(E1, v1, v0); 410 if(flip_surface) { 411 f3_cross(N, E1, E0); 412 } else { 413 f3_cross(N, E0, E1); 414 } 415 B = f3_normalize(N, N) * 0.5f; /* Base area */ 416 h = -f3_dot(N, v0); /* Height from the base to the apex */ 417 volume += (h*B); 418 } 419 return (float)(volume / 3.0); 420 } 421 422 res_T 423 mesh_setup_indexed_vertices 424 (struct mesh* mesh, 425 const unsigned ntris, 426 void (*get_indices)(const unsigned itri, unsigned ids[3], void* ctx), 427 const unsigned nverts, 428 struct s3d_vertex_data attribs[], 429 const unsigned nattribs, 430 void* data) 431 { 432 unsigned iattr; 433 char has_position = 0; 434 res_T res = RES_OK; 435 ASSERT(mesh); 436 437 if(!ntris || !nverts || !attribs || !nattribs) { 438 res = RES_BAD_ARG; 439 goto error; 440 } 441 442 /* Check indices description */ 443 if(get_indices == S3D_KEEP) { 444 if(!mesh->indices) { /* No indice was previously set */ 445 res = RES_BAD_ARG; 446 goto error; 447 } else { 448 const size_t nids_prev = darray_u32_size_get(&mesh->indices->data); 449 const size_t ntris_prev = nids_prev / 3; 450 if(ntris_prev != ntris) { /* Inconsistant data */ 451 res = RES_BAD_ARG; 452 goto error; 453 } 454 } 455 } 456 457 /* Check the vertex data description */ 458 iattr = 0; 459 has_position = 0; 460 FOR_EACH(iattr, 0, nattribs) { 461 if((unsigned)attribs[iattr].usage >= S3D_ATTRIBS_COUNT__) { /* Invalid usage */ 462 res = RES_BAD_ARG; 463 goto error; 464 } 465 if(attribs[iattr].get == S3D_KEEP) { 466 const enum s3d_attrib_usage attr_usage = attribs[iattr].usage; 467 const enum s3d_type type = attribs[iattr].type; 468 if(!mesh->attribs[attr_usage]) { /* The vertex attrib was no set */ 469 res = RES_BAD_ARG; 470 goto error; 471 } else { 472 const enum s3d_type type_prev = mesh->attribs_type[attr_usage]; 473 const struct darray_float* attr = &mesh->attribs[attr_usage]->data; 474 size_t nverts_prev = darray_float_size_get(attr); 475 nverts_prev /= s3d_type_get_dimension(type_prev); 476 if(type_prev != type || nverts_prev != nverts) { /* Inconsistant data */ 477 res = RES_BAD_ARG; 478 goto error; 479 } 480 } 481 } 482 if(attribs[iattr].usage == S3D_POSITION) 483 has_position = 1; 484 } 485 486 if(!has_position) { /* The vertex must have a position */ 487 res = RES_BAD_ARG; 488 goto error; 489 } 490 491 mesh_setup_indices(mesh, ntris, get_indices, nverts, data); 492 493 /* Setup vertex data */ 494 FOR_EACH(iattr, 0, nattribs) { 495 if(attribs[iattr].usage == S3D_POSITION) { 496 mesh_setup_positions(mesh, nverts, attribs + iattr, data); 497 } else { 498 mesh_setup_attribs(mesh, nverts, attribs + iattr, data); 499 } 500 } 501 502 exit: 503 return res; 504 error: 505 goto exit; 506 } 507 508 void 509 mesh_compute_aabb(struct mesh* mesh, float lower[3], float upper[3]) 510 { 511 float* pos; 512 size_t ivert, nverts; 513 ASSERT(mesh && lower && upper); 514 515 f3_splat(lower, FLT_MAX); 516 f3_splat(upper,-FLT_MAX); 517 518 nverts = mesh_get_nverts(mesh); 519 if(!nverts) return; 520 521 pos = mesh_get_pos(mesh); 522 FOR_EACH(ivert, 0, nverts) { 523 const size_t ipos = ivert * 3; 524 f3_min(lower, lower, pos + ipos); 525 f3_max(upper, upper, pos + ipos); 526 } 527 } 528 529 void 530 mesh_copy_indexed_vertices(const struct mesh* src, struct mesh* dst) 531 { 532 int i; 533 ASSERT(src && dst && src != dst); 534 535 /* Release the previous index buffer of dst */ 536 if(dst->indices) { 537 index_buffer_ref_put(dst->indices); 538 dst->indices = NULL; 539 } 540 /* Get a reference onto the index buffer of src */ 541 if(src->indices) { 542 index_buffer_ref_get(src->indices); 543 dst->indices = src->indices; 544 } 545 546 FOR_EACH(i, 0, S3D_ATTRIBS_COUNT__) { 547 /* Release the previous vertex buffers of dst */ 548 if(dst->attribs[i]) { 549 vertex_buffer_ref_put(dst->attribs[i]); 550 dst->attribs[i] = NULL; 551 } 552 /* Get a reference onto the vertex buffers of src */ 553 if(src->attribs[i]) { 554 vertex_buffer_ref_get(src->attribs[i]); 555 dst->attribs[i] = src->attribs[i]; 556 dst->attribs_type[i] = src->attribs_type[i]; 557 } 558 } 559 } 560