star-3daw

Create star-3d geometries from OBJ files
git clone git://git.meso-star.fr/star-3daw.git
Log | Files | Refs | README | LICENSE

s3daw.c (13582B)


      1 /* Copyright (C) 2015, 2016, 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 #define _POSIX_C_SOURCE 200112L /* dirname support */
     17 
     18 #include "s3daw.h"
     19 
     20 #include <rsys/dynamic_array_uint.h>
     21 #include <rsys/dynamic_array_float.h>
     22 #include <rsys/float3.h>
     23 #include <rsys/float4.h>
     24 #include <rsys/hash_table.h>
     25 #include <rsys/logger.h>
     26 #include <rsys/mem_allocator.h>
     27 #include <rsys/ref_count.h>
     28 
     29 #include <star/s3d.h>
     30 
     31 #include <aw.h>
     32 #include <polygon.h>
     33 #include <string.h>
     34 
     35 #define DARRAY_NAME shape
     36 #define DARRAY_DATA struct s3d_shape*
     37 #include <rsys/dynamic_array.h>
     38 
     39 #define HTABLE_NAME vertex_id
     40 #define HTABLE_DATA unsigned
     41 #define HTABLE_KEY size_t
     42 #include <rsys/hash_table.h>
     43 
     44 #define HTABLE_NAME material
     45 #define HTABLE_DATA struct aw_material
     46 #define HTABLE_KEY struct str
     47 #define HTABLE_KEY_FUNCTOR_EQ str_eq
     48 #define HTABLE_KEY_FUNCTOR_HASH str_hash
     49 #define HTABLE_KEY_FUNCTOR_INIT str_init
     50 #define HTABLE_KEY_FUNCTOR_COPY str_copy
     51 #define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release
     52 #define HTABLE_KEY_FUNCTOR_RELEASE str_release
     53 #include <rsys/hash_table.h>
     54 
     55 struct s3daw {
     56   struct aw_obj* loader_obj;
     57   struct aw_mtl* loader_mtl;
     58   struct polygon* polygon;
     59   struct s3d_device* s3d;
     60 
     61   struct darray_shape shapes;
     62 
     63   int verbose;
     64 
     65   struct mem_allocator* allocator;
     66   struct logger* logger;
     67   ref_T ref;
     68 };
     69 
     70 struct context {
     71   const struct darray_float* positions;
     72   const struct darray_uint* indices;
     73 };
     74 
     75 /*******************************************************************************
     76  * Helper functions
     77  ******************************************************************************/
     78 static void
     79 get_indices(const unsigned itri, unsigned ids[3], void* context)
     80 {
     81   struct context* ctx = context;
     82   const unsigned* indices;
     83   ASSERT(ids && ctx && itri < darray_uint_size_get(ctx->indices)/3);
     84   indices = darray_uint_cdata_get(ctx->indices) + itri*3;
     85   ids[0] = indices[0];
     86   ids[1] = indices[1];
     87   ids[2] = indices[2];
     88 }
     89 
     90 static void
     91 get_position(const unsigned ivert, float pos[3], void* context)
     92 {
     93   struct context* ctx = context;
     94   const float* position;
     95   ASSERT(ctx && pos && ivert < darray_float_size_get(ctx->positions)/3);
     96   position = darray_float_cdata_get(ctx->positions) + ivert*3;
     97   pos[0] = position[0];
     98   pos[1] = position[1];
     99   pos[2] = position[2];
    100 }
    101 
    102 static INLINE void
    103 shapes_clear(struct darray_shape* shapes)
    104 {
    105   size_t i;
    106   ASSERT(shapes);
    107   FOR_EACH(i, 0, darray_shape_size_get(shapes)) {
    108     S3D(shape_ref_put(darray_shape_data_get(shapes)[i]));
    109   }
    110   darray_shape_clear(shapes);
    111 }
    112 
    113 static res_T
    114 shape_register
    115   (struct s3daw* s3daw,
    116    const struct aw_obj_named_group* obj_mtl,
    117    struct darray_float* vertices,
    118    struct darray_uint* indices,
    119    struct htable_vertex_id* vertices_ids)
    120 {
    121   struct context ctx;
    122   struct s3d_vertex_data vertex_data;
    123   struct s3d_shape* shape = NULL;
    124   size_t iface;
    125   size_t ntris, nverts;
    126   res_T res = RES_OK;
    127   ASSERT(s3daw && obj_mtl && vertices && indices && vertices_ids);
    128 
    129   /* Reset the scrach shape data */
    130   darray_float_clear(vertices);
    131   darray_uint_clear(indices);
    132   htable_vertex_id_clear(vertices_ids);
    133 
    134   FOR_EACH(iface, obj_mtl->face_id, obj_mtl->face_id + obj_mtl->faces_count) {
    135     struct aw_obj_face face;
    136     struct aw_obj_vertex vertex;
    137     struct aw_obj_vertex_data vdata;
    138     const uint32_t* tri_ids;
    139     uint32_t ntri_ids;
    140     size_t ivertex;
    141     size_t i;
    142 
    143     AW(obj_get_face(s3daw->loader_obj, iface, &face));
    144 
    145     /* Triangulate the face */
    146     POLYGON(clear(s3daw->polygon));
    147     FOR_EACH(ivertex, face.vertex_id, face.vertex_id + face.vertices_count) {
    148       float position[3];
    149       AW(obj_get_vertex(s3daw->loader_obj, ivertex, &vertex));
    150       AW(obj_get_vertex_data(s3daw->loader_obj, &vertex, &vdata));
    151       position[0] = (float)vdata.position[0];
    152       position[1] = (float)vdata.position[1];
    153       position[2] = (float)vdata.position[2];
    154       res = polygon_vertex_add(s3daw->polygon, position);
    155       if(res != RES_OK) goto error;
    156     }
    157     res = polygon_triangulate(s3daw->polygon, &tri_ids, &ntri_ids);
    158     if(res != RES_OK) goto error;
    159 
    160     FOR_EACH(i, 0, ntri_ids) {
    161       unsigned* ivertex_registered;
    162 
    163       /* Define if the obj_face vertex is already registered */
    164       ivertex = tri_ids[i] + face.vertex_id;
    165       AW(obj_get_vertex(s3daw->loader_obj, ivertex, &vertex));
    166       ivertex_registered = htable_vertex_id_find(vertices_ids, &vertex.position_id);
    167 
    168       if(ivertex_registered) {
    169         /* Vertex is registered. Simply add its id to the indices */
    170         res = darray_uint_push_back(indices, ivertex_registered);
    171         if(res != RES_OK) goto error;
    172       } else {
    173         /* Vertex is not registered. Register it and add its id to the indices */
    174         float position[3];
    175         const unsigned ivertex_new = (unsigned)darray_float_size_get(vertices)/3;
    176 
    177         AW(obj_get_vertex_data(s3daw->loader_obj, &vertex, &vdata));
    178         position[0] = (float)vdata.position[0];
    179         position[1] = (float)vdata.position[1];
    180         position[2] = (float)vdata.position[2];
    181 
    182         #define CALL(Func) if((res = Func) != RES_OK) goto error;
    183         CALL(darray_float_push_back(vertices, position+0));
    184         CALL(darray_float_push_back(vertices, position+1));
    185         CALL(darray_float_push_back(vertices, position+2));
    186         CALL(darray_uint_push_back(indices, &ivertex_new));
    187         CALL(htable_vertex_id_set(vertices_ids, &vertex.position_id, &ivertex_new));
    188         #undef CALL
    189       }
    190     }
    191   }
    192 
    193   nverts = darray_float_size_get(vertices) / 3;
    194   ntris = darray_uint_size_get(indices) / 3;
    195   if(!ntris) goto exit; /* Nothing to do */
    196 
    197   res = s3d_shape_create_mesh(s3daw->s3d, &shape);
    198   if(res != RES_OK) goto error;
    199 
    200   vertex_data.usage = S3D_POSITION;
    201   vertex_data.type = S3D_FLOAT3;
    202   vertex_data.get = get_position;
    203   ctx.indices = indices;
    204   ctx.positions = vertices;
    205 
    206   /* Setup the S3D mesh data */
    207   res = s3d_mesh_setup_indexed_vertices(shape, (unsigned)ntris, get_indices,
    208     (unsigned)nverts, &vertex_data, 1, &ctx);
    209   if(res != RES_OK) goto error;
    210 
    211   /* Register the shape */
    212   res = darray_shape_push_back(&s3daw->shapes, &shape);
    213   if(res != RES_OK) goto error;
    214 
    215 exit:
    216   return res;
    217 error:
    218   if(shape) S3D(shape_ref_put(shape));
    219   goto exit;
    220 }
    221 
    222 static res_T
    223 shapes_create(struct s3daw* s3daw, const char* filename)
    224 {
    225   struct darray_uint indices;
    226   struct darray_float vertices;
    227   struct htable_vertex_id vertices_ids;
    228   struct aw_obj_desc obj_desc;
    229   struct aw_obj_named_group grp;
    230   res_T res = RES_OK;
    231   ASSERT(s3daw && filename);
    232 
    233   /* Clean up the previously created shapes */
    234   shapes_clear(&s3daw->shapes);
    235 
    236   /* Init scratch data structures */
    237   darray_uint_init(s3daw->allocator, &indices);
    238   darray_float_init(s3daw->allocator, &vertices);
    239   htable_vertex_id_init(s3daw->allocator, &vertices_ids);
    240 
    241   AW(obj_get_desc(s3daw->loader_obj, &obj_desc));
    242 
    243   if(obj_desc.faces_count == 0) {
    244     if(s3daw->verbose) {
    245       logger_print(s3daw->logger, LOG_WARNING,
    246         "Empty file content `%s'. It is discarded.\n", filename);
    247     }
    248     goto exit;
    249   }
    250 
    251   if(!obj_desc.usemtls_count) { /* No material grouping => triangle soup */
    252     grp.name = NULL;
    253     grp.face_id = 0;
    254     grp.faces_count = obj_desc.faces_count;
    255     res = shape_register(s3daw, &grp, &vertices, &indices, &vertices_ids);
    256     if(res != RES_OK) goto error;
    257   } else { /* Create a S3D shape per material */
    258     size_t imtl;
    259     FOR_EACH(imtl, 0, obj_desc.usemtls_count) {
    260       AW(obj_get_mtl(s3daw->loader_obj, imtl, &grp));
    261       res = shape_register(s3daw, &grp, &vertices, &indices, &vertices_ids);
    262       if(res != RES_OK) goto error;
    263     }
    264   }
    265 
    266 exit:
    267   darray_uint_release(&indices);
    268   darray_float_release(&vertices);
    269   htable_vertex_id_release(&vertices_ids);
    270   return res;
    271 error:
    272   shapes_clear(&s3daw->shapes);
    273   goto exit;
    274 }
    275 
    276 static void
    277 release_s3daw(ref_T* ref)
    278 {
    279   struct s3daw* s3daw = CONTAINER_OF(ref, struct s3daw, ref);
    280   ASSERT(ref);
    281   if(s3daw->loader_obj) AW(obj_ref_put(s3daw->loader_obj));
    282   if(s3daw->loader_mtl) AW(mtl_ref_put(s3daw->loader_mtl));
    283   if(s3daw->polygon) POLYGON(ref_put(s3daw->polygon));
    284   if(s3daw->s3d) S3D(device_ref_put(s3daw->s3d));
    285   shapes_clear(&s3daw->shapes);
    286   darray_shape_release(&s3daw->shapes);
    287   MEM_RM(s3daw->allocator, s3daw);
    288 }
    289 
    290 /*******************************************************************************
    291  * Exported functions
    292  ******************************************************************************/
    293 res_T
    294 s3daw_create
    295   (struct logger* logger,
    296    struct mem_allocator* allocator,
    297    struct aw_obj* obj,
    298    struct aw_mtl* mtl,
    299    struct s3d_device* s3d,
    300    const int verbose,
    301    struct s3daw** out_s3daw)
    302 {
    303   struct s3daw* s3daw = NULL;
    304   struct mem_allocator* mem_allocator;
    305   res_T res = RES_OK;
    306 
    307   if(!s3d || !out_s3daw) {
    308     res = RES_BAD_ARG;
    309     goto error;
    310   }
    311   mem_allocator = allocator ? allocator : &mem_default_allocator;
    312   s3daw = MEM_CALLOC(mem_allocator, 1, sizeof(struct s3daw));
    313   if(!s3daw) {
    314     res = RES_MEM_ERR;
    315     goto error;
    316   }
    317 
    318   ref_init(&s3daw->ref);
    319   s3daw->allocator = mem_allocator;
    320   s3daw->logger = logger ? logger : LOGGER_DEFAULT;
    321   s3daw->verbose = verbose;
    322   S3D(device_ref_get(s3d));
    323   s3daw->s3d = s3d;
    324   darray_shape_init(s3daw->allocator, &s3daw->shapes);
    325 
    326   res = polygon_create(s3daw->allocator, &s3daw->polygon);
    327   if(res != RES_OK) goto error;
    328 
    329   if(obj) {
    330     AW(obj_ref_get(obj));
    331     s3daw->loader_obj = obj;
    332   } else {
    333     res = aw_obj_create(logger, allocator, verbose, &s3daw->loader_obj);
    334     if(res != RES_OK) goto error;
    335   }
    336 
    337   if(mtl) {
    338     AW(mtl_ref_get(mtl));
    339     s3daw->loader_mtl = mtl;
    340   } else {
    341     res = aw_mtl_create(logger, allocator, verbose, &s3daw->loader_mtl);
    342     if(res != RES_OK) goto error;
    343   }
    344 
    345 exit:
    346   if(out_s3daw) *out_s3daw = s3daw;
    347   return res;
    348 error:
    349   if(s3daw) {
    350     S3DAW(ref_put(s3daw));
    351     s3daw = NULL;
    352   }
    353   goto exit;
    354 }
    355 
    356 res_T
    357 s3daw_ref_get(struct s3daw* s3daw)
    358 {
    359   if(!s3daw) return RES_BAD_ARG;
    360   ref_get(&s3daw->ref);
    361   return RES_OK;
    362 }
    363 
    364 res_T
    365 s3daw_ref_put(struct s3daw* s3daw)
    366 {
    367   if(!s3daw) return RES_BAD_ARG;
    368   ref_put(&s3daw->ref, release_s3daw);
    369   return RES_OK;
    370 }
    371 
    372 res_T
    373 s3daw_get_loaders(struct s3daw* s3daw, struct aw_obj** obj, struct aw_mtl** mtl)
    374 {
    375   if(!s3daw) return RES_BAD_ARG;
    376   if(obj) *obj = s3daw->loader_obj;
    377   if(mtl) *mtl = s3daw->loader_mtl;
    378   return RES_OK;
    379 }
    380 
    381 res_T
    382 s3daw_get_s3d_device(struct s3daw* s3daw, struct s3d_device** s3d)
    383 {
    384   if(!s3daw || !s3d) return RES_BAD_ARG;
    385   *s3d = s3daw->s3d;
    386   return RES_OK;
    387 }
    388 
    389 res_T
    390 s3daw_load(struct s3daw* s3daw, const char* filename)
    391 {
    392   res_T res;
    393 
    394   if(!s3daw || !filename) return RES_BAD_ARG;
    395 
    396   res = aw_obj_load(s3daw->loader_obj, filename);
    397   if(res != RES_OK) return res;
    398   return shapes_create(s3daw, filename);
    399 }
    400 
    401 res_T
    402 s3daw_load_stream(struct s3daw* s3daw, FILE* stream)
    403 {
    404   res_T res = RES_OK;
    405 
    406   if(!s3daw || !stream) return RES_BAD_ARG;
    407 
    408   res = aw_obj_load_stream(s3daw->loader_obj, stream, "stream");
    409   if(res != RES_OK) return res;
    410   return shapes_create(s3daw, "./");
    411 }
    412 
    413 res_T
    414 s3daw_clear(struct s3daw* s3daw)
    415 {
    416   if(!s3daw) return RES_BAD_ARG;
    417 
    418   /* Clear intermediary data structures */
    419   POLYGON(clear(s3daw->polygon));
    420 
    421   /* Clear shape */
    422   shapes_clear(&s3daw->shapes);
    423 
    424   AW(obj_clear(s3daw->loader_obj));
    425   AW(mtl_clear(s3daw->loader_mtl));
    426 
    427   return RES_OK;
    428 }
    429 
    430 res_T
    431 s3daw_get_shapes_count(const struct s3daw* s3daw, size_t* nshapes)
    432 {
    433   if(!s3daw || !nshapes) return RES_BAD_ARG;
    434   *nshapes = darray_shape_size_get(&s3daw->shapes);
    435   return RES_OK;
    436 }
    437 
    438 res_T
    439 s3daw_get_shape
    440   (struct s3daw* s3daw,
    441    const size_t ishape,
    442    struct s3d_shape** shape)
    443 {
    444   if(!s3daw || !shape || ishape >= darray_shape_size_get(&s3daw->shapes))
    445     return RES_BAD_ARG;
    446   *shape = darray_shape_data_get(&s3daw->shapes)[ishape];
    447   return RES_OK;
    448 }
    449 
    450 res_T
    451 s3daw_attach_to_scene(struct s3daw* s3daw, struct s3d_scene* scene)
    452 {
    453   struct s3d_shape* shape = NULL;
    454   size_t i=0, n=0;
    455   res_T res = RES_OK;
    456 
    457   if(!s3daw) {
    458     res = RES_BAD_ARG;
    459     goto error;
    460   }
    461 
    462   S3DAW(get_shapes_count(s3daw, &n));
    463   FOR_EACH(i, 0, n) {
    464     S3DAW(get_shape(s3daw, i, &shape));
    465     res = s3d_scene_attach_shape(scene, shape);
    466     if(res != RES_OK) goto error;
    467   }
    468 
    469 exit:
    470   return res;
    471 error:
    472   /* Rollback the attachments */
    473   n = i;
    474   FOR_EACH(i, 0, n) {
    475     S3DAW(get_shape(s3daw, i, &shape));
    476     S3D(scene_detach_shape(scene, shape));
    477   }
    478   goto exit;
    479 }
    480 
    481 res_T
    482 s3daw_detach_from_scene(struct s3daw* s3daw, struct s3d_scene* scene)
    483 {
    484   struct s3d_shape* shape = NULL;
    485   size_t i=0, n=0;
    486   res_T res = RES_OK;
    487 
    488   if(!s3daw) {
    489     res = RES_BAD_ARG;
    490     goto error;
    491   }
    492 
    493   S3DAW(get_shapes_count(s3daw, &n));
    494   FOR_EACH(i, 0, n) {
    495     S3DAW(get_shape(s3daw, i, &shape));
    496     res = s3d_scene_detach_shape(scene, shape);
    497     if(res != RES_OK) goto error;
    498   }
    499 
    500 exit:
    501   return res;
    502 error:
    503   /* Rollback the detachments */
    504   n = i;
    505   FOR_EACH(i, 0, n) {
    506     S3DAW(get_shape(s3daw, i, &shape));
    507     S3D(scene_attach_shape(scene, shape));
    508   }
    509   goto exit;
    510 }
    511