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