polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

ply.c (6121B)


      1 
      2 #include <assert.h>
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include "file.h"
      7 #include "mdl.h"
      8 #include "common.h"
      9 #include "ply.h"
     10 #include "vec3.h"
     11 
     12 /* void parse_vertex( */
     13 
     14 
     15 enum ply_state {
     16     PLY_MAGIC,
     17     PLY_HEADER,
     18     PLY_VERTICES,
     19     PLY_INDICES
     20 };
     21 
     22 static void next_line(const char **cursor) {
     23     while (*((*cursor)++) != '\n')
     24         ;
     25 }
     26 
     27 static int consume_string(const char **cursor, const char *str) {
     28     int len = strlen(str);
     29 
     30     if (memcmp(*cursor, str, len) == 0)
     31         *cursor += len;
     32     else
     33         return 0;
     34 
     35     return 1;
     36 }
     37 
     38 static int parse_element(const char **cursor, const char *element, int *nverts) {
     39     int ok;
     40     static char buffer[32];
     41 
     42     snprintf(buffer, sizeof(buffer), "element %s ", element);
     43 
     44     ok = consume_string(cursor, buffer);
     45 
     46     if (!ok)
     47         return 0;
     48 
     49     *nverts = atoi(*cursor);
     50 
     51     return *nverts != 0;
     52 }
     53 
     54 
     55 static inline int parse_vertex(const char **cursor, float *v, float *n, u8 *c) {
     56     static float st[2];
     57 
     58     int matched =
     59         sscanf(*cursor, "%f %f %f %f %f %f %f %f %hhu %hhu %hhu",
     60             &v[0], &v[1], &v[2],
     61             &n[0], &n[1], &n[2],
     62             &st[0], &st[1],
     63             &c[0], &c[1], &c[2]);
     64 
     65     if (matched == 11)
     66         return 1;
     67 
     68     matched =
     69         sscanf(*cursor, "%f %f %f %f %f %f %hhu %hhu %hhu",
     70                &v[0], &v[1], &v[2],
     71                &n[0], &n[1], &n[2],
     72                &c[0], &c[1], &c[2]);
     73 
     74     if (matched == 9)
     75         return 1;
     76 
     77     return 0;
     78 }
     79 
     80 
     81 static int parse_indices(const char **cursor, int *inds) {
     82     // NOTE: only support tris inds for now
     83     int matched =
     84         sscanf(*cursor, "3 %d %d %d", &inds[0], &inds[1], &inds[2]);
     85 
     86     return matched == 3;
     87 }
     88 
     89 static int parse_header(const char **cursor, int *nverts, int *ninds) {
     90     int ok = 0;
     91     ok = parse_element(cursor, "vertex", nverts);
     92     if (ok) return 1;
     93 
     94     ok = parse_element(cursor, "face", ninds);
     95     if (ok) return 1;
     96 
     97     return 0;
     98 }
     99 
    100 static int parse_magic(const char **cursor) {
    101     return consume_string(cursor, "ply");
    102 }
    103 
    104 
    105 int parse_ply(const char *filename, struct geometry *geom)
    106 {
    107     struct mdl_geometry mdlgeom;
    108     init_mdl_geometry(&mdlgeom);
    109     struct make_geometry *mkgeom = &mdlgeom.mkgeom;
    110 
    111     int ok = parse_ply_with_mkgeom(filename, &mdlgeom);
    112 
    113     make_buffer_geometry(mkgeom, geom);
    114     free_make_geometry(mkgeom);
    115 
    116     return ok;
    117 }
    118 
    119 int parse_ply_with_mkgeom(const char *filename, struct mdl_geometry *mdlgeom)
    120 {
    121     size_t len;
    122     int success = 0;
    123     int nverts = 0;
    124     int ninds = 0;
    125     int done = 0;
    126     int res = 0;
    127     int cvert = 0;
    128     int cind = 0;
    129 
    130     struct make_geometry *mkgeom = &mdlgeom->mkgeom;
    131     enum ply_state state = PLY_MAGIC;
    132     const char *data = file_contents(filename, &len);
    133     const char *p = data;
    134 
    135     float vert[3], norm[3], min[3]={0}, max[3]={0};
    136     int inds[3];
    137     u8 color[3];
    138 
    139     while(!done) {
    140         switch (state) {
    141         case PLY_MAGIC:
    142             res = parse_magic(&p);
    143             if (!res) {
    144                 printf("failed to parse ply magic header\n");
    145                 done = 1;
    146                 break;
    147             }
    148             state = PLY_HEADER;
    149             break;
    150         case PLY_HEADER:
    151             res = parse_header(&p, &nverts, &ninds);
    152             if (consume_string(&p, "end_header")) {
    153                 if (ninds == 0 || nverts == 0)  {
    154                     printf("ply parsing failed, could not determine number "
    155                            " of vertices or faces\n");
    156                     done = 1;
    157                     break;
    158                 }
    159 
    160                 mkgeom->vertices = calloc(nverts * 3, sizeof(*mkgeom->vertices));
    161                 mkgeom->normals  = calloc(nverts * 3, sizeof(*mkgeom->normals));
    162                 mkgeom->colors   = calloc(nverts * 3, sizeof(*mkgeom->colors));
    163                 mkgeom->indices  = calloc(ninds * 3, sizeof(*mkgeom->indices));
    164 
    165                 state = PLY_VERTICES;
    166             }
    167             break;
    168         case PLY_VERTICES:
    169             res = parse_vertex(&p, vert, norm, color);
    170             if (!res) {
    171                 printf("failed parsing verts\n");
    172                 done = 1;
    173                 break;
    174             }
    175 
    176             // compute bounding box as we go
    177             if (cvert == 0) {
    178                 vec3_copy(vert, min);
    179                 vec3_copy(vert, max);
    180             }
    181             else {
    182                 vec3_min(vert, min, min);
    183                 vec3_max(vert, max, max);
    184             }
    185 
    186             mkgeom->vertices[cvert * 3]     = vert[0];
    187             mkgeom->vertices[cvert * 3 + 1] = vert[1];
    188             mkgeom->vertices[cvert * 3 + 2] = vert[2];
    189 
    190             mkgeom->normals[cvert * 3]     = norm[0];
    191             mkgeom->normals[cvert * 3 + 1] = norm[1];
    192             mkgeom->normals[cvert * 3 + 2] = norm[2];
    193 
    194             mkgeom->colors[cvert * 3]     = color[0] / 255.0;
    195             mkgeom->colors[cvert * 3 + 1] = color[1] / 255.0;
    196             mkgeom->colors[cvert * 3 + 2] = color[2] / 255.0;
    197 
    198             cvert++;
    199 
    200             if (cvert == nverts)
    201                 state = PLY_INDICES;
    202 
    203             break;
    204         case PLY_INDICES:
    205             res = parse_indices(&p, inds);
    206             if (!res) {
    207                 printf("failed parsing indices\n");
    208                 done = 1;
    209                 break;
    210             }
    211 
    212             mkgeom->indices[cind * 3]     = inds[0];
    213             mkgeom->indices[cind * 3 + 1] = inds[1];
    214             mkgeom->indices[cind * 3 + 2] = inds[2];
    215 
    216             cind++;
    217 
    218             if (cind == ninds) {
    219                 success = 1;
    220                 done = 1;
    221             }
    222 
    223             break;
    224         }
    225 
    226         // next line
    227         if (p >= data + len) {
    228             /* printf("got here, state %d cind %d ninds %d over %ld\n", */
    229             /*        state, cind, ninds, p - (data + len)); */
    230             done = 1;
    231         }
    232 
    233         if (!done)
    234             next_line(&p);
    235     }
    236 
    237     free((void*)data);
    238 
    239     if (success) {
    240         mkgeom->num_indices = ninds * 3;
    241         mkgeom->num_verts = nverts;
    242 
    243         vec3_copy(min, mdlgeom->min);
    244         vec3_copy(max, mdlgeom->max);
    245     }
    246 
    247     return success;
    248 }