star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

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