loader_aw

Load OBJ/MTL file formats
git clone git://git.meso-star.fr/loader_aw.git
Log | Files | Refs | README | LICENSE

aw_obj.c (26376B)


      1 /* Copyright (C) 2014-2017, 2020-2023 Vincent Forest (vaplv@free.fr)
      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 Lesser General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU Lesser General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #define _POSIX_C_SOURCE 200112L /* strtok_r support */
     17 
     18 #include "aw_c.h"
     19 
     20 #include <rsys/cstr.h>
     21 #include <rsys/dynamic_array_double.h>
     22 #include <rsys/logger.h>
     23 #include <rsys/mem_allocator.h>
     24 #include <rsys/ref_count.h>
     25 #include <rsys/str.h>
     26 #include <rsys/text_reader.h>
     27 
     28 #include <float.h> /* DBL_MAX & DBL_MIN definition */
     29 #include <stdarg.h>
     30 
     31 #ifdef COMPILER_CL
     32   #pragma warning(push)
     33   #pragma warning(disable:4706) /* Assignment within a condition */
     34 #endif
     35 
     36 static const char* MSG_PREFIX_INFO = "load-obj:info: ";
     37 static const char* MSG_PREFIX_ERROR = "load-obj:error: ";
     38 static const char* MSG_PREFIX_WARNING = "load-obj:warning: ";
     39 
     40 /* Generate the darray_vertex data structure */
     41 #define DARRAY_NAME vertex
     42 #define DARRAY_DATA struct aw_obj_vertex
     43 #include <rsys/dynamic_array.h>
     44 
     45 /* Generate the darray_face data structure */
     46 #define DARRAY_NAME face
     47 #define DARRAY_DATA struct aw_obj_face
     48 #include <rsys/dynamic_array.h>
     49 
     50 static const struct aw_obj_vertex VERTEX_NULL = {
     51   AW_ID_NONE, AW_ID_NONE, AW_ID_NONE
     52 };
     53 
     54 struct named_group {
     55   struct str name;
     56   size_t face_id; /* Index of the first group face */
     57   size_t faces_count;
     58 };
     59 
     60 static INLINE void
     61 named_group_init(struct mem_allocator* allocator, struct named_group* grp)
     62 {
     63   str_init(allocator, &grp->name);
     64 }
     65 
     66 static INLINE void
     67 named_group_release(struct named_group* grp)
     68 {
     69   ASSERT(grp);
     70   str_release(&grp->name);
     71 }
     72 
     73 static INLINE res_T
     74 named_group_copy(struct named_group* dst, const struct named_group* src)
     75 {
     76   ASSERT(dst && src);
     77   if(dst == src) return RES_OK;
     78   dst->face_id = src->face_id;
     79   dst->faces_count = src->faces_count;
     80   return str_copy(&dst->name, &src->name);
     81 }
     82 
     83 static INLINE res_T
     84 named_group_copy_and_release(struct named_group* dst, struct named_group* src)
     85 {
     86   ASSERT(dst && src);
     87   if(dst == src) return RES_OK;
     88   dst->face_id = src->face_id;
     89   dst->faces_count = src->faces_count;
     90   return str_copy_and_release(&dst->name, &src->name);
     91 }
     92 
     93 /* Generate the darray_named_group data structure */
     94 #define DARRAY_NAME named_group
     95 #define DARRAY_DATA struct named_group
     96 #define DARRAY_FUNCTOR_INIT named_group_init
     97 #define DARRAY_FUNCTOR_RELEASE named_group_release
     98 #define DARRAY_FUNCTOR_COPY named_group_copy
     99 #define DARRAY_FUNCTOR_COPY_AND_RELEASE named_group_copy_and_release
    100 #include <rsys/dynamic_array.h>
    101 
    102 /* Generate the darray_smooth_group data structure */
    103 #define DARRAY_NAME smooth_group
    104 #define DARRAY_DATA struct aw_obj_smooth_group
    105 #include <rsys/dynamic_array.h>
    106 
    107 /* Generate the darray_mtllib data structure */
    108 #define DARRAY_NAME mtllib
    109 #define DARRAY_DATA struct str
    110 #define DARRAY_FUNCTOR_INIT str_init
    111 #define DARRAY_FUNCTOR_RELEASE str_release
    112 #define DARRAY_FUNCTOR_COPY str_copy
    113 #define DARRAY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release
    114 #include <rsys/dynamic_array.h>
    115 
    116 struct aw_obj {
    117   struct darray_double positions; /* double4 */
    118   struct darray_double normals; /* double3 */
    119   struct darray_double texcoords; /* double3 */
    120   struct darray_vertex vertices;
    121   struct darray_face faces;
    122   struct darray_named_group groups;
    123   struct darray_smooth_group smooth_groups;
    124   struct darray_named_group usemtls;
    125   struct darray_mtllib mtllibs;
    126 
    127   size_t igroups_active; /* Index toward the first active group */
    128 
    129   ref_T ref;
    130   struct mem_allocator* allocator;
    131   struct logger* logger;
    132   struct logger logger__; /* Default logger */
    133   int verbose;
    134 };
    135 
    136 /*******************************************************************************
    137  * Helper functions
    138  ******************************************************************************/
    139 static INLINE void
    140 log_msg
    141   (const struct aw_obj* obj,
    142    const enum log_type stream,
    143    const char* msg,
    144    va_list vargs)
    145 {
    146   ASSERT(obj && msg);
    147   if(obj->verbose) {
    148     res_T res; (void)res;
    149     res = logger_vprint(obj->logger, stream, msg, vargs);
    150     ASSERT(res == RES_OK);
    151   }
    152 }
    153 
    154 static INLINE void
    155 log_err(const struct aw_obj* obj, const char* msg, ...)
    156 {
    157   va_list vargs_list;
    158   ASSERT(obj && msg);
    159 
    160   va_start(vargs_list, msg);
    161   log_msg(obj, LOG_ERROR, msg, vargs_list);
    162   va_end(vargs_list);
    163 }
    164 
    165 static INLINE void
    166 log_warn(const struct aw_obj* obj, const char* msg, ...)
    167 {
    168   va_list vargs_list;
    169   ASSERT(obj && msg);
    170 
    171   va_start(vargs_list, msg);
    172   log_msg(obj, LOG_WARNING, msg, vargs_list);
    173   va_end(vargs_list);
    174 }
    175 
    176 static INLINE void
    177 flush_groups(struct aw_obj* obj)
    178 {
    179   size_t nfaces, ngrps;
    180   ASSERT(obj);
    181 
    182   nfaces = darray_face_size_get(&obj->faces);
    183   if(!nfaces) return; /* No face to flush */
    184 
    185   ngrps = darray_named_group_size_get(&obj->groups);
    186   if(!ngrps) return; /* No group */
    187 
    188   /* There should be an active group to flush if ngrps is not null */
    189   ASSERT(obj->igroups_active < ngrps);
    190 
    191   /* Setup the number of faces of each active group */
    192   FOR_EACH(obj->igroups_active, obj->igroups_active, ngrps) {
    193     struct named_group* grp;
    194     grp = darray_named_group_data_get(&obj->groups) + obj->igroups_active;
    195     ASSERT(grp->face_id <= nfaces);
    196     grp->faces_count = nfaces - grp->face_id;
    197   }
    198 }
    199 
    200 static INLINE void
    201 flush_usemtl(struct aw_obj* obj)
    202 {
    203   struct named_group* mtl;
    204   size_t nfaces, ngrps;
    205   ASSERT(obj);
    206 
    207   nfaces = darray_face_size_get(&obj->faces);
    208   if(!nfaces) return; /* No face to flush */
    209 
    210   ngrps = darray_named_group_size_get(&obj->usemtls);
    211   if(!ngrps) return; /* No group */
    212 
    213   /* Setup the number of faces of the current mtl */
    214   mtl = darray_named_group_data_get(&obj->usemtls) + (ngrps - 1);
    215   ASSERT(mtl->face_id <= nfaces);
    216   mtl->faces_count = nfaces - mtl->face_id;
    217 }
    218 
    219 static INLINE void
    220 flush_smooth_group(struct aw_obj* obj)
    221 {
    222   struct aw_obj_smooth_group* grp;
    223   size_t nfaces, ngrps;
    224   ASSERT(obj);
    225 
    226   nfaces = darray_face_size_get(&obj->faces);
    227   if(!nfaces) return; /* No face to flush */
    228 
    229   ngrps = darray_smooth_group_size_get(&obj->smooth_groups);
    230   if(!ngrps) return; /* No smooth group */
    231 
    232   /* Setup the number of faces of the current smoothed group */
    233   grp = darray_smooth_group_data_get(&obj->smooth_groups) + (ngrps - 1);
    234   ASSERT(grp->face_id <= nfaces);
    235   grp->faces_count = nfaces - grp->face_id;
    236 }
    237 
    238 static res_T
    239 parse_doubleX_in_darray
    240   (struct darray_double* darray,
    241    const unsigned int count_min,
    242    const unsigned int count_max,
    243    const double default_value,
    244    char** tk_ctx)
    245 {
    246   res_T res = RES_OK;
    247   size_t i;
    248   ASSERT(darray);
    249 
    250   i = darray_double_size_get(darray);
    251 
    252   res = darray_double_resize(darray, i + count_max);
    253   if(res != RES_OK) goto error;
    254 
    255   res = parse_doubleX(darray_double_data_get(darray) + i, count_min, count_max,
    256     -DBL_MAX, DBL_MAX, default_value, tk_ctx);
    257   if(res != RES_OK) goto error;
    258 
    259 exit:
    260   return res;
    261 error:
    262   darray_double_resize(darray, i);
    263   goto exit;
    264 }
    265 
    266 static res_T
    267 string_to_vertex_id
    268   (const char* str, /* Input string */
    269    const size_t vertices_count, /* Current vertices count for a given attrib */
    270    size_t* id) /* Computed id */
    271 {
    272   long id_long;
    273   res_T res;
    274 
    275   res = cstr_to_long(str, &id_long);
    276   if(res != RES_OK) return res;
    277 
    278   if(id_long > 0) {
    279     /* The obj indexation starts at 1 rather than 0 => subtract 1 to the vertex
    280      * attribute indices in order to match the C memory layout */
    281     *id = (size_t)(id_long - 1);
    282   } else if(id_long < 0) {
    283     /* One can count vertices back up the list from an element's position in
    284      * the file. In this case the index is negative. For instance, a reference
    285      * number of -1 indicates the vertex immediately above the element */
    286     *id = vertices_count - (size_t)labs(id_long);
    287   }
    288   if(*id >= vertices_count)
    289     return RES_BAD_ARG;
    290   return RES_OK;
    291 }
    292 
    293 static res_T
    294 parse_face_vertex
    295   (struct aw_obj* obj,
    296    struct aw_obj_face* face,
    297    char* str)
    298 {
    299   struct aw_obj_vertex vert = VERTEX_NULL;
    300   char* tk1 = NULL;
    301   char* tk2 = NULL;
    302   char* tk3 = NULL;
    303   char* tk_ctx = NULL;
    304   size_t npositions = 0;
    305   size_t nnormals = 0;
    306   size_t ntexcoords = 0;
    307   res_T res = RES_OK;
    308   ASSERT(obj && face && str);
    309 
    310   npositions = darray_double_size_get(&obj->positions) / 4;
    311   nnormals = darray_double_size_get(&obj->normals) / 3;
    312   ntexcoords = darray_double_size_get(&obj->texcoords) / 3;
    313 
    314   #define CALL(Func) if(RES_OK != (res = Func)) goto error
    315 
    316   /* Parse the position */
    317   tk1 = strtok_r(str, "/", &tk_ctx);
    318   ASSERT(tk1);
    319   CALL(string_to_vertex_id(tk1, npositions, &vert.position_id));
    320 
    321   tk2 = strtok_r(NULL, "/", &tk_ctx);
    322   if(tk2) {
    323     tk1 += strlen(tk1);
    324     if(tk2 > tk1 + 3) { /* Unexpected N `/' separators with N > 2 */
    325       res = RES_BAD_ARG;
    326       goto error;
    327     }
    328 
    329     if(tk2 == tk1 + 2) { /* `//' separator => No tex */
    330       /* Parse the normal */
    331       CALL(string_to_vertex_id(tk2, nnormals, &vert.normal_id));
    332     } else {
    333       /* Parse the texcoords and */
    334       CALL(string_to_vertex_id(tk2, ntexcoords, &vert.texcoord_id));
    335 
    336       tk3 = strtok_r(NULL, "", &tk_ctx);
    337       if(tk3) {
    338         /* Parse the normal */
    339         CALL(string_to_vertex_id(tk3, nnormals, &vert.normal_id));
    340       }
    341     }
    342   }
    343 
    344   /* Register the vertex */
    345   CALL(darray_vertex_push_back(&obj->vertices, &vert));
    346   ++face->vertices_count;
    347 
    348   #undef CALL
    349 exit:
    350   return res;
    351 error:
    352   goto exit;
    353 }
    354 
    355 static res_T
    356 parse_face(struct aw_obj* obj, char** tk_ctx)
    357 {
    358   struct aw_obj_face face;
    359   char* tk = NULL;
    360   res_T res = RES_OK;
    361   ASSERT(obj && tk_ctx);
    362 
    363   face.vertex_id = darray_vertex_size_get(&obj->vertices);
    364   face.vertices_count = 0;
    365   face.group_id = darray_named_group_size_get(&obj->groups) - 1;
    366   face.smooth_group_id = darray_smooth_group_size_get(&obj->smooth_groups) - 1;
    367   face.mtl_id = darray_named_group_size_get(&obj->usemtls) - 1;
    368 
    369   while((tk = strtok_r(NULL, " \t", tk_ctx))) {
    370     res = parse_face_vertex(obj, &face, tk);
    371     if(res != RES_OK) goto error;
    372   }
    373 
    374   /* Register the face */
    375   res = darray_face_push_back(&obj->faces, &face);
    376   if(res != RES_OK) goto error;
    377 
    378 exit:
    379   return res;
    380 error:
    381   /* Release the registered faces */
    382   CHK(darray_vertex_resize(&obj->vertices, face.vertex_id) == RES_OK);
    383   goto exit;
    384 }
    385 
    386 static res_T
    387 parse_group(struct aw_obj* obj, char** tk_ctx)
    388 {
    389   char* tk;
    390   size_t ngrps = 0;
    391   size_t igrp = 0;
    392   res_T res = RES_OK;
    393   ASSERT(obj && tk_ctx);
    394 
    395   flush_groups(obj);
    396 
    397   ngrps = igrp = darray_named_group_size_get(&obj->groups);
    398   obj->igroups_active = igrp;
    399 
    400   tk = strtok_r(NULL, " \t", tk_ctx); /* May be NULL */
    401 
    402   do {
    403     struct named_group* grp = NULL;
    404 
    405     /* Allocate a group */
    406     res = darray_named_group_resize(&obj->groups, igrp + 1);
    407     if(res != RES_OK) goto error;
    408 
    409     /* Fetch the group */
    410     grp = darray_named_group_data_get(&obj->groups) + igrp;
    411     ++igrp;
    412 
    413     /* Setup the group name */
    414     res = str_set(&grp->name, tk ? tk : "default");
    415     if(res != RES_OK) goto error;
    416 
    417     /* Initialize the group face indices */
    418     grp->face_id = darray_face_size_get(&obj->faces);
    419     grp->faces_count = 0;
    420   } while((tk = strtok_r(NULL, " \t", tk_ctx)));
    421 
    422 exit:
    423   return res;
    424 error:
    425   /* Release the created groups */
    426   CHK(darray_named_group_resize(&obj->groups, ngrps) == RES_OK);
    427   goto exit;
    428 }
    429 
    430 static res_T
    431 parse_smooth_group(struct aw_obj* obj, char** tk_ctx)
    432 {
    433   struct aw_obj_smooth_group grp;
    434   char* tk = NULL;
    435   res_T res;
    436   ASSERT(obj && tk_ctx);
    437 
    438   flush_smooth_group(obj);
    439 
    440   tk = strtok_r(NULL, " \t", tk_ctx);
    441   if(!tk) return RES_BAD_ARG;
    442 
    443   if(!strcmp(tk, "off")) {
    444     grp.is_smoothed = 0;
    445   } else if(!strcmp(tk, "on")) {
    446     grp.is_smoothed = 1;
    447   } else {
    448     int i;
    449     res = cstr_to_int(tk, &i);
    450     if(res != RES_OK) return res;
    451     grp.is_smoothed = i != 0;
    452   }
    453 
    454   /* Initialize the smoot group face indices */
    455   grp.face_id = darray_face_size_get(&obj->faces);
    456   grp.faces_count = 0;
    457 
    458   /* Register the smooth group */
    459   res = darray_smooth_group_push_back(&obj->smooth_groups, &grp);
    460   if(res != RES_OK) return res;
    461 
    462   return RES_OK;
    463 }
    464 
    465 static res_T
    466 parse_mtllib(struct aw_obj* obj, char** tk_ctx)
    467 {
    468   char* tk = NULL;
    469   size_t imtllib = 0;
    470   size_t nmtllibs = 0;
    471   res_T res = RES_OK;
    472   ASSERT(obj && tk_ctx);
    473 
    474   nmtllibs = imtllib = darray_mtllib_size_get(&obj->mtllibs);
    475 
    476   tk = strtok_r(NULL, " \t", tk_ctx);
    477   if(!tk) {
    478     res = RES_BAD_ARG;
    479     goto error;
    480   }
    481 
    482   do {
    483     struct str* str = NULL;
    484 
    485     /* Allocate the mtllib path */
    486     res = darray_mtllib_resize(&obj->mtllibs, imtllib + 1);
    487     if(res != RES_OK) goto error;
    488 
    489     /* Fetc the mtllib path */
    490     str = darray_mtllib_data_get(&obj->mtllibs) + imtllib;
    491     ++imtllib;
    492 
    493     /* Setup the mtllib name */
    494     res = str_set(str, tk);
    495     if(res != RES_OK) goto error;
    496 
    497   } while((tk = strtok_r(NULL, " \t", tk_ctx)));
    498 
    499 exit:
    500   return res;
    501 error:
    502   CHK(darray_mtllib_resize(&obj->mtllibs, nmtllibs) == RES_OK);
    503   goto exit;
    504 }
    505 
    506 static res_T
    507 parse_usemtl(struct aw_obj* obj, char** tk_ctx)
    508 {
    509   char* tk = NULL;
    510   struct named_group* mtl = NULL;
    511   size_t nmtls;
    512   res_T res = RES_OK;
    513   ASSERT(obj && tk_ctx);
    514 
    515   flush_usemtl(obj);
    516 
    517   tk = strtok_r(NULL, " \t", tk_ctx); /* Blanks are prohibited in mtl name */
    518   if(!tk) {
    519     res = RES_BAD_ARG;
    520     goto error;
    521   }
    522 
    523   nmtls = darray_named_group_size_get(&obj->usemtls);
    524 
    525   /* Allocate the material */
    526   res = darray_named_group_resize(&obj->usemtls, nmtls + 1);
    527   if(res != RES_OK) goto error;
    528 
    529   /* Fetch the material */
    530   mtl = darray_named_group_data_get(&obj->usemtls) + nmtls;
    531 
    532   /* Setup the material name */
    533   res = str_set(&mtl->name, tk);
    534   if(res != RES_OK) goto error;
    535 
    536   /* Initialize the material face indices */
    537   mtl->face_id = darray_face_size_get(&obj->faces);
    538   mtl->faces_count = 0;
    539 
    540 exit:
    541   return res;
    542 error:
    543   if(mtl) darray_named_group_pop_back(&obj->usemtls);
    544   goto exit;
    545 }
    546 
    547 static res_T
    548 parse_obj_line(struct aw_obj* obj, struct txtrdr* txtrdr)
    549 {
    550   char* tk = NULL;
    551   char* tk_ctx = NULL;
    552   res_T res = RES_OK;
    553   ASSERT(obj && txtrdr);
    554 
    555   tk = strtok_r(txtrdr_get_line(txtrdr), " \t", &tk_ctx);
    556   ASSERT(tk); /* A line should exist since it was parsed by txtrdr */
    557 
    558   if(!strcmp(tk, "v")) { /* Vertex position */
    559     res = parse_doubleX_in_darray(&obj->positions, 3, 4, 1.f, &tk_ctx);
    560   } else if(!strcmp(tk, "vn")) { /* Vertex normal */
    561     res = parse_doubleX_in_darray(&obj->normals, 3, 3, 0.f, &tk_ctx);
    562   } else if(!strcmp(tk, "vt")) { /* Vertex texture coordinates */
    563     res = parse_doubleX_in_darray(&obj->texcoords, 1, 3, 0.f, &tk_ctx);
    564   } else if(!strcmp(tk, "f") || !strcmp(tk, "fo")) { /* face element */
    565     res = parse_face(obj, &tk_ctx);
    566   } else if(!strcmp(tk, "g")) { /* Grouping */
    567     res = parse_group(obj, &tk_ctx);
    568   } else if(!strcmp(tk, "s")) { /* Smooth group */
    569     res = parse_smooth_group(obj, &tk_ctx);
    570   } else if(!strcmp(tk, "mtllib")) { /* Mtl library */
    571     res = parse_mtllib(obj, &tk_ctx);
    572   } else if(!strcmp(tk, "usemtl")) { /* Use the mtl library */
    573     res = parse_usemtl(obj, &tk_ctx);
    574   } else {
    575     log_warn(obj,
    576       "%s:%lu: warning: ignored or malformed directive `%s'.\n",
    577       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    578     strtok_r(NULL, "", &tk_ctx); /* Discard remaining text */
    579   }
    580   if(res != RES_OK) {
    581     log_err(obj, "%s:%lu: parsing failed.\n",
    582       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    583     goto error;
    584   }
    585 
    586   tk = strtok_r(NULL, "", &tk_ctx);
    587   if(tk) {
    588     log_err(obj, "%s:%lu: unexpected text `%s'.\n",
    589       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    590     res = RES_BAD_ARG;
    591     goto error;
    592   }
    593 
    594 exit:
    595   return res;
    596 error:
    597   goto exit;
    598 }
    599 
    600 static res_T
    601 load_stream(struct aw_obj* obj, FILE* stream, const char* stream_name)
    602 {
    603   struct txtrdr* txtrdr = NULL;
    604   res_T res = RES_OK;
    605   ASSERT(obj && stream && stream_name);
    606 
    607   AW(obj_clear(obj));
    608 
    609   res = txtrdr_stream(obj->allocator, stream, stream_name, '#', &txtrdr);
    610   if(res != RES_OK) {
    611     log_err(obj, "Could not create the text reader.\n");
    612     goto error;
    613   }
    614 
    615   for(;;) {
    616     res = txtrdr_read_line(txtrdr);
    617     if(res != RES_OK) {
    618       log_err(obj, "%s: could not read the line `%lu'.\n",
    619         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    620       goto error;
    621     }
    622 
    623     if(!txtrdr_get_cline(txtrdr)) break; /* No more parsed line */
    624 
    625     res = parse_obj_line(obj, txtrdr);
    626     if(res != RES_OK) goto error;
    627   }
    628 
    629   flush_groups(obj);
    630   flush_smooth_group(obj);
    631   flush_usemtl(obj);
    632 exit:
    633   if(txtrdr) txtrdr_ref_put(txtrdr);
    634   return res;
    635 error:
    636   AW(obj_clear(obj));
    637   goto exit;
    638 }
    639 
    640 static void
    641 obj_release(ref_T* ref)
    642 {
    643   struct aw_obj* obj = CONTAINER_OF(ref, struct aw_obj, ref);
    644   ASSERT(ref);
    645   darray_double_release(&obj->positions);
    646   darray_double_release(&obj->normals);
    647   darray_double_release(&obj->texcoords);
    648   darray_vertex_release(&obj->vertices);
    649   darray_face_release(&obj->faces);
    650   darray_named_group_release(&obj->groups);
    651   darray_named_group_release(&obj->usemtls);
    652   darray_smooth_group_release(&obj->smooth_groups);
    653   darray_mtllib_release(&obj->mtllibs);
    654   if(obj->logger == &obj->logger__) logger_release(&obj->logger__);
    655   MEM_RM(obj->allocator, obj);
    656 }
    657 
    658 /*******************************************************************************
    659  * Exported functions
    660  ******************************************************************************/
    661 res_T
    662 aw_obj_create
    663   (struct logger* logger,
    664    struct mem_allocator* mem_allocator,
    665    const int verbose,
    666    struct aw_obj** obj_out)
    667 {
    668   struct mem_allocator* allocator;
    669   struct aw_obj* obj = NULL;
    670   res_T res = RES_OK;
    671 
    672   if(!obj_out) {
    673     res = RES_BAD_ARG;
    674     goto error;
    675   }
    676   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
    677   obj = MEM_CALLOC(allocator, 1, sizeof(struct aw_obj));
    678   if(!obj) {
    679     res = RES_MEM_ERR;
    680     goto error;
    681   }
    682   ref_init(&obj->ref);
    683   obj->allocator = allocator;
    684   obj->verbose = verbose;
    685   darray_double_init(mem_allocator, &obj->positions);
    686   darray_double_init(mem_allocator, &obj->normals);
    687   darray_double_init(mem_allocator, &obj->texcoords);
    688   darray_vertex_init(mem_allocator, &obj->vertices);
    689   darray_face_init(mem_allocator, &obj->faces);
    690   darray_named_group_init(mem_allocator, &obj->groups);
    691   darray_named_group_init(mem_allocator, &obj->usemtls);
    692   darray_smooth_group_init(mem_allocator, &obj->smooth_groups);
    693   darray_mtllib_init(mem_allocator, &obj->mtllibs);
    694 
    695   if(logger) {
    696     obj->logger = logger;
    697   } else {
    698     res = setup_default_logger(obj->allocator, &obj->logger__,
    699       MSG_PREFIX_INFO, MSG_PREFIX_ERROR, MSG_PREFIX_WARNING);
    700     if(res != RES_OK) goto error;
    701     obj->logger = &obj->logger__;
    702   }
    703 
    704 exit:
    705   if(obj_out)
    706     *obj_out = obj;
    707   return res;
    708 error:
    709   if(obj) {
    710     AW(obj_ref_put(obj));
    711     obj = NULL;
    712   }
    713   goto exit;
    714 }
    715 
    716 res_T
    717 aw_obj_ref_get(struct aw_obj* obj)
    718 {
    719   if(!obj) return RES_BAD_ARG;
    720   ref_get(&obj->ref);
    721   return RES_OK;
    722 }
    723 
    724 res_T
    725 aw_obj_ref_put(struct aw_obj* obj)
    726 {
    727   if(!obj) return RES_BAD_ARG;
    728   ref_put(&obj->ref, obj_release);
    729   return RES_OK;
    730 }
    731 
    732 res_T
    733 aw_obj_load(struct aw_obj* obj, const char* filename)
    734 {
    735   FILE* fp = NULL;
    736   res_T res = RES_OK;
    737 
    738   if(!obj || !filename) {
    739     res = RES_BAD_ARG;
    740     goto error;
    741   }
    742 
    743   fp = fopen(filename, "r");
    744   if(!fp) {
    745     log_err(obj, "Error opening `%s'.\n", filename);
    746     res = RES_IO_ERR;
    747     goto error;
    748   }
    749 
    750   res = load_stream(obj, fp, filename);
    751   if(res != RES_OK) goto error;
    752 
    753 exit:
    754   if(fp) fclose(fp);
    755   return res;
    756 error:
    757   goto exit;
    758 }
    759 
    760 res_T
    761 aw_obj_load_stream(struct aw_obj* obj, FILE* stream, const char* stream_name)
    762 {
    763   res_T res = RES_OK;
    764 
    765   if(!obj || !stream) {
    766     res = RES_BAD_ARG;
    767     goto error;
    768   }
    769 
    770   res = load_stream(obj, stream, stream_name ? stream_name : "stream");
    771   if(res != RES_OK) goto error;
    772 
    773 exit:
    774   return res;
    775 error:
    776   goto exit;
    777 }
    778 
    779 res_T
    780 aw_obj_clear(struct aw_obj* obj)
    781 {
    782   if(!obj) return RES_BAD_ARG;
    783   darray_double_clear(&obj->positions);
    784   darray_double_clear(&obj->normals);
    785   darray_double_clear(&obj->texcoords);
    786   darray_vertex_clear(&obj->vertices);
    787   darray_face_clear(&obj->faces);
    788   darray_named_group_clear(&obj->groups);
    789   darray_named_group_clear(&obj->usemtls);
    790   darray_smooth_group_clear(&obj->smooth_groups);
    791   darray_mtllib_clear(&obj->mtllibs);
    792   obj->igroups_active = 0;
    793   return RES_OK;
    794 }
    795 
    796 res_T
    797 aw_obj_purge(struct aw_obj* obj)
    798 {
    799   if(!obj) return RES_BAD_ARG;
    800   darray_double_purge(&obj->positions);
    801   darray_double_purge(&obj->normals);
    802   darray_double_purge(&obj->texcoords);
    803   darray_vertex_purge(&obj->vertices);
    804   darray_face_purge(&obj->faces);
    805   darray_named_group_purge(&obj->groups);
    806   darray_named_group_purge(&obj->usemtls);
    807   darray_smooth_group_purge(&obj->smooth_groups);
    808   darray_mtllib_purge(&obj->mtllibs);
    809   obj->igroups_active = 0;
    810   return RES_OK;
    811 }
    812 
    813 res_T
    814 aw_obj_get_desc(const struct aw_obj* obj, struct aw_obj_desc* desc)
    815 {
    816   if(!obj || !desc)
    817     return RES_BAD_ARG;
    818   desc->faces_count = darray_face_size_get(&obj->faces);
    819   desc->positions_count = darray_double_size_get(&obj->positions) / 4;
    820   desc->normals_count = darray_double_size_get(&obj->normals) / 3;
    821   desc->texcoords_count = darray_double_size_get(&obj->texcoords) / 3;
    822   desc->groups_count = darray_named_group_size_get(&obj->groups);
    823   desc->smooth_groups_count = darray_smooth_group_size_get(&obj->smooth_groups);
    824   desc->usemtls_count = darray_named_group_size_get(&obj->usemtls);
    825   desc->mtllibs_count = darray_mtllib_size_get(&obj->mtllibs);
    826   return RES_OK;
    827 }
    828 
    829 res_T
    830 aw_obj_get_face
    831   (const struct aw_obj* obj,
    832    const size_t iface,
    833    struct aw_obj_face* face)
    834 {
    835   if(!obj || !face || iface >= darray_face_size_get(&obj->faces))
    836     return RES_BAD_ARG;
    837   *face = darray_face_cdata_get(&obj->faces)[iface];
    838   return RES_OK;
    839 }
    840 
    841 res_T
    842 aw_obj_get_group
    843   (const struct aw_obj* obj,
    844    const size_t igroup,
    845    struct aw_obj_named_group* grp)
    846 {
    847   const struct named_group* src = NULL;
    848   if(!obj || !grp || igroup >= darray_named_group_size_get(&obj->groups))
    849     return RES_BAD_ARG;
    850   src = darray_named_group_cdata_get(&obj->groups) + igroup;
    851   grp->name = str_cget(&src->name);
    852   grp->face_id = src->face_id;
    853   grp->faces_count = src->faces_count;
    854   return RES_OK;
    855 }
    856 
    857 res_T
    858 aw_obj_get_smooth_group
    859   (const struct aw_obj* obj,
    860    const size_t ismooth_group,
    861    struct aw_obj_smooth_group* group)
    862 {
    863   if(!obj || !group
    864   || ismooth_group >= darray_smooth_group_size_get(&obj->smooth_groups))
    865     return RES_BAD_ARG;
    866   *group = darray_smooth_group_cdata_get(&obj->smooth_groups)[ismooth_group];
    867   return RES_OK;
    868 }
    869 
    870 res_T
    871 aw_obj_get_mtl
    872   (const struct aw_obj* obj,
    873    const size_t imtl,
    874    struct aw_obj_named_group* mtl)
    875 {
    876   const struct named_group* src;
    877   if(!obj || !mtl || imtl >= darray_named_group_size_get(&obj->usemtls))
    878     return RES_BAD_ARG;
    879   src = darray_named_group_cdata_get(&obj->usemtls) + imtl;
    880   mtl->name = str_cget(&src->name);
    881   mtl->face_id = src->face_id;
    882   mtl->faces_count = src->faces_count;
    883   return RES_OK;
    884 }
    885 
    886 res_T
    887 aw_obj_get_mtllib
    888   (const struct aw_obj* obj,
    889    const size_t imtllib,
    890    const char** mtllib)
    891 {
    892   const struct str* str;
    893   if(!obj || !mtllib || imtllib >= darray_mtllib_size_get(&obj->mtllibs))
    894     return RES_BAD_ARG;
    895   str = darray_mtllib_cdata_get(&obj->mtllibs) + imtllib;
    896   *mtllib = str_cget(str);
    897   return RES_OK;
    898 }
    899 
    900 res_T
    901 aw_obj_get_vertex
    902   (const struct aw_obj* obj,
    903    const size_t ivertex,
    904    struct aw_obj_vertex* vertex)
    905 {
    906   if(!obj || !vertex || ivertex >= darray_vertex_size_get(&obj->vertices))
    907     return RES_BAD_ARG;
    908   *vertex = darray_vertex_cdata_get(&obj->vertices)[ivertex];
    909   return RES_OK;
    910 }
    911 
    912 res_T
    913 aw_obj_get_vertex_data
    914   (const struct aw_obj* obj,
    915    const struct aw_obj_vertex* vertex,
    916    struct aw_obj_vertex_data* vertex_data)
    917 {
    918   const double* data;
    919   res_T res = RES_OK;
    920 
    921   if(!obj || !vertex || !vertex_data) {
    922     res = RES_BAD_ARG;
    923     goto error;
    924   }
    925   if(vertex->position_id >= darray_double_size_get(&obj->positions) / 4) {
    926     res = RES_BAD_ARG;
    927     goto error;
    928   }
    929   if(vertex->texcoord_id != VERTEX_NULL.texcoord_id
    930   && vertex->texcoord_id >= darray_double_size_get(&obj->texcoords) / 3) {
    931     res = RES_BAD_ARG;
    932     goto error;
    933   }
    934   if(vertex->normal_id != VERTEX_NULL.normal_id
    935   && vertex->normal_id >= darray_double_size_get(&obj->normals) / 3) {
    936     res = RES_BAD_ARG;
    937     goto error;
    938   }
    939 
    940   /* Fetch vertex position */
    941   data = darray_double_cdata_get(&obj->positions) + vertex->position_id * 4;
    942   vertex_data->position[0] = data[0];
    943   vertex_data->position[1] = data[1];
    944   vertex_data->position[2] = data[2];
    945   vertex_data->position[3] = data[3];
    946 
    947   /* Setup vertex texcoord */
    948   if(vertex->texcoord_id == VERTEX_NULL.texcoord_id) {
    949     vertex_data->texcoord[0] = 0;
    950     vertex_data->texcoord[1] = 0;
    951     vertex_data->texcoord[2] = 0;
    952   } else {
    953     data = darray_double_cdata_get(&obj->texcoords) + vertex->texcoord_id * 3;
    954     vertex_data->texcoord[0] = data[0];
    955     vertex_data->texcoord[1] = data[1];
    956     vertex_data->texcoord[2] = data[2];
    957   }
    958   /* Setup vertex normal */
    959   if(vertex->normal_id == VERTEX_NULL.normal_id) {
    960     vertex_data->normal[0] = 0;
    961     vertex_data->normal[1] = 0;
    962     vertex_data->normal[2] = 0;
    963   } else {
    964     data = darray_double_cdata_get(&obj->normals) + vertex->normal_id * 3;
    965     vertex_data->normal[0] = data[0];
    966     vertex_data->normal[1] = data[1];
    967     vertex_data->normal[2] = data[2];
    968   }
    969 
    970 exit:
    971   return res;
    972 error:
    973   goto exit;
    974 }
    975 
    976 res_T
    977 aw_obj_get_positions(const struct aw_obj* obj, const double** positions)
    978 {
    979   if(!obj || !positions) return RES_BAD_ARG;
    980   *positions = darray_double_cdata_get(&obj->positions);
    981   return RES_OK;
    982 }
    983 
    984 res_T
    985 aw_obj_get_texcoords(const struct aw_obj* obj, const double** texcoords)
    986 {
    987   if(!obj || !texcoords) return RES_BAD_ARG;
    988   *texcoords = darray_double_cdata_get(&obj->texcoords);
    989   return RES_OK;
    990 }
    991 
    992 res_T
    993 aw_obj_get_normals(const struct aw_obj* obj, const double** normals)
    994 {
    995   if(!obj || !normals) return RES_BAD_ARG;
    996   *normals = darray_double_cdata_get(&obj->normals);
    997   return RES_OK;
    998 }
    999 
   1000 #ifdef COMPILER_CL
   1001   #pragma warning(pop)
   1002 #endif
   1003