star-3d

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

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