78bdc53524b93c1f65a362dd5c25627d3e60bc58
[summerhack] / src / 3dengfx / src / 3dengfx / ply.cpp
1 #include <vector>
2 #include <string>
3 #include <cstdio>
4 #include <cctype>
5 #include <cassert>
6 #include "gfx/3dgeom.hpp"
7 #include "common/err_msg.h"
8
9 #define BUFFER_SIZE             256
10
11 using std::vector;
12 using std::string;
13
14 enum PlyFormat {
15         PLY_ASCII,
16         PLY_LITTLE_ENDIAN,
17         PLY_BIG_ENDIAN
18 };
19
20 enum PropType {
21         PROP_FLOAT,
22         PROP_INT8,
23         PROP_INT16,
24         PROP_INT32,
25         PROP_LIST
26 };
27
28 const size_t prop_size[] = {32, 8, 16, 32, 0};
29
30 struct PropTypeMatch {
31         char *symb;
32         PropType type;
33 } prop_match[] = {
34         {"float",       PROP_FLOAT},
35         {"float32",     PROP_FLOAT},
36         {"int",         PROP_INT32},
37         {"int32",       PROP_INT32},
38         {"uint",        PROP_INT32},
39         {"uint32",      PROP_INT32},
40         {"int16",       PROP_INT16},
41         {"uint16",      PROP_INT16},
42         {"char",        PROP_INT8},
43         {"uchar",       PROP_INT8},
44         {"int8",        PROP_INT8},
45         {"uint8",       PROP_INT8},
46         {"list",        PROP_LIST},
47         {0, (PropType)0}
48 };
49
50 struct Property {
51         string name;
52         PropType type;
53         PropType list_type;     // list elements type, if type == PROP_LIST
54         size_t size;
55 };
56
57 enum ElementType {ELEM_UNKNOWN, ELEM_VERTEX, ELEM_FACE};
58
59 struct Element {
60         ElementType type;
61         unsigned long count;
62         vector<Property> prop;
63         long offset;
64 };
65
66 struct Ply {
67         PlyFormat fmt;
68         vector<Element> elem;
69         FILE *fp;
70         unsigned long header_skip;
71 };
72
73 static Ply *read_header(FILE *fp);
74 static Element *seek_elem(Ply *ply, ElementType elem_type);
75
76 static const char *ply_filename = 0;    // for error reports
77
78 bool file_is_ply(FILE *file) {
79         char sig[5] = {0};
80         
81         fseek(file, 0, SEEK_SET);
82         fgets(sig, 5, file);
83
84         return !strcmp(sig, "ply\n");
85 }
86
87 #define FAIL(m) {\
88         error("ply(%s): " m, fname);\
89         fclose(fp);\
90         delete ply;\
91         return 0;\
92 }
93
94 TriMesh *load_mesh_ply(const char *fname) {
95         const char *sep = " \t\n";
96         char buf[BUFFER_SIZE];
97
98         FILE *fp = fopen(fname, "r");
99         if(!fp || !file_is_ply(fp)) {
100                 if(fp) fclose(fp);
101                 return 0;
102         }
103
104         ply_filename = fname;
105
106         Ply *ply = read_header(fp);
107         if(!ply) {
108                 fclose(fp);
109                 return 0;
110         }
111         ply->fp = fp;
112
113         vector<Vertex> verts;
114         vector<Triangle> tris;
115
116         Element *elem;
117
118         // -- read the vertices
119         if(!(elem = seek_elem(ply, ELEM_VERTEX))) {
120                 FAIL("failed to locate vertex data");
121         }
122
123         if(elem->prop[0].type != PROP_FLOAT || elem->prop[1].type != PROP_FLOAT || elem->prop[2].type != PROP_FLOAT) {
124                 FAIL("weird vertex format, didn't find 3 floats");
125         }
126
127         for(unsigned long i=0; i<elem->count; i++) {
128                 Vertex v;
129                 if(ply->fmt == PLY_ASCII) {
130                         fgets(buf, BUFFER_SIZE, fp);
131
132                         char *x_str = strtok(buf, sep);
133                         char *y_str = strtok(0, sep);
134                         char *z_str = strtok(0, sep);
135
136                         if(!x_str || !y_str || !z_str) {
137                                 FAIL("vertex data loading failed, format inconsistent");
138                         }
139                         v.pos.x = atof(x_str);
140                         v.pos.y = atof(y_str);
141                         v.pos.z = -atof(z_str);
142                         
143                 } else {
144                         FAIL("sorry binary ply loading not implemented yet");
145                 }
146
147                 verts.push_back(v);
148         }
149
150         // -- read the face list
151         if(!(elem = seek_elem(ply, ELEM_FACE))) {
152                 FAIL("failed to locate face data");
153         }
154
155         if(elem->prop[0].type != PROP_LIST) {
156                 FAIL("weird face format, didn't find an index list");
157         }
158
159         for(unsigned long i=0; i<elem->count; i++) {
160                 int count;
161                 unsigned long indices[4];
162                 
163                 if(ply->fmt == PLY_ASCII) {
164                         fgets(buf, BUFFER_SIZE, fp);
165
166                         count = atoi(strtok(buf, sep));
167                         if(count < 3 || count > 4) {
168                                 FAIL("only triangles and quads are supported");
169                         }
170
171                         for(int j=0; j<count; j++) {
172                                 char *substr = strtok(0, sep);
173                                 if(!substr) {
174                                         FAIL("inconsistent face list data");
175                                 }
176                                 indices[j] = atol(substr);
177                         }
178                         
179                 } else {
180                         FAIL("sorry binary ply loading not implemented yet");
181                 }
182
183                 Triangle tri(indices[0], indices[1], indices[2]);
184                 tris.push_back(tri);
185
186                 if(count == 4) {
187                         Triangle tri2(indices[0], indices[2], indices[3]);
188                         tris.push_back(tri2);
189                 }
190         }
191
192         fclose(fp);
193         delete ply;
194
195         // ok now we have the vertex/triangle vectors, let's create the mesh and return it
196         TriMesh *mesh = new TriMesh(&verts[0], verts.size(), &tris[0], tris.size());
197         mesh->calculate_normals();
198         return mesh;
199 }
200
201 static Ply *read_header(FILE *fp) {
202         const char *sep = " \t\n";
203         char buf[BUFFER_SIZE];
204         
205         fseek(fp, 0, SEEK_SET);
206
207         Ply *ply = new Ply;
208         memset(ply, 0, sizeof(Ply));
209
210         bool vertex_ok = false, face_ok = false;
211
212         while(fgets(buf, BUFFER_SIZE, fp)) {
213                 char *field = strtok(buf, sep);
214                 if(!field) continue;
215
216                 if(!strcmp(field, "format")) {
217                         char *fmt = strtok(0, sep);
218                         if(!fmt) {
219                                 error("ply(%s): invalid format field", ply_filename);
220                                 delete ply;
221                                 return 0;
222                         }
223
224                         if(!strcmp(fmt, "ascii")) {
225                                 ply->fmt = PLY_ASCII;
226                         } else if(!strcmp(fmt, "binary_little_endian")) {
227                                 ply->fmt = PLY_LITTLE_ENDIAN;
228                         } else if(!strcmp(fmt, "binary_big_endian")) {
229                                 ply->fmt = PLY_BIG_ENDIAN;
230                         } else {
231                                 error("ply(%s): invalid format field", ply_filename);
232                                 delete ply;
233                                 return 0;
234                         }
235                         
236                 } else if(!strcmp(field, "element")) {
237                         char *elem_name = strtok(0, sep);
238                         if(!elem_name) {
239                                 warning("ply(%s): invalid element definition", ply_filename);
240                                 continue;
241                         }
242                         
243                         char *count_str = strtok(0, sep);
244                         if(!count_str || !isdigit(*count_str)) {
245                                 error("ply(%s): element not followed by a count", ply_filename);
246                                 delete ply;
247                                 return 0;
248                         }
249
250                         unsigned long count = atol(count_str);
251
252                         Element elem;
253                         elem.type = ELEM_UNKNOWN;
254                         elem.count = count;
255                                                 
256                         if(!strcmp(elem_name, "vertex")) {
257                                 elem.type = ELEM_VERTEX;
258                                 vertex_ok = true;
259                         }
260                         
261                         if(!strcmp(elem_name, "face")) {
262                                 elem.type = ELEM_FACE;
263                                 face_ok = true;
264                         }
265
266                         // determine element properties
267                         while((buf[0] = fgetc(fp)) == 'p') {
268                                 if(!fgets(buf + 1, BUFFER_SIZE - 1, fp)) {
269                                         error("ply(%s): unexpected end of file while reading element properties", ply_filename);
270                                         delete ply;
271                                         return 0;
272                                 }
273                                 char *ptr = strtok(buf, sep);
274                                 if(!ptr || strcmp(ptr, "property")) {
275                                         error("ply(%s): looking for \"propery\", got \"%s\"", ply_filename, ptr ? ptr : "NULL");
276                                         delete ply;
277                                         return 0;
278                                 }
279
280                                 Property prop;
281                                 prop.size = 0;
282                                 
283                                 /*
284                                 char *name = strtok(0, sep);
285                                 if(!name) {
286                                         error("ply(%s): invalid property entry, no name specified", ply_filename);
287                                         delete ply;
288                                         return 0;
289                                 }
290                                 prop.name = name;
291                                 */
292
293                                 char *type = strtok(0, sep);
294                                 if(!type) {
295                                         error("ply(%s): invalid property entry, no type specified", ply_filename);
296                                         delete ply;
297                                         return 0;
298                                 }
299
300                                 PropTypeMatch *mptr = prop_match;
301                                 while(mptr->symb) {
302                                         if(!strcmp(type, mptr->symb)) {
303                                                 prop.type = mptr->type;
304                                                 prop.size = prop_size[prop.type];
305                                                 break;
306                                         }
307                                         mptr++;
308                                 }
309
310                                 if(prop.type == PROP_LIST) {
311                                         type = strtok(0, sep);
312                                         if(!type) {
313                                                 error("ply(%s): invalid property entry, no list subtype specified", ply_filename);
314                                                 delete ply;
315                                                 return 0;
316                                         }
317                                         
318                                         mptr = prop_match;
319                                         while(mptr->symb) {
320                                                 if(!strcmp(type, mptr->symb)) {
321                                                         prop.list_type = mptr->type;
322                                                         prop.size = prop_size[prop.list_type];
323                                                         break;
324                                                 }
325                                                 mptr++;
326                                         }
327                                 }
328
329                                 if(!prop.size) {
330                                         error("ply(%s): unknown property type \"%s\"", ply_filename, type);
331                                         delete ply;
332                                         return 0;
333                                 }
334
335                                 elem.prop.push_back(prop);
336                         }
337
338                         ungetc(buf[0], fp);
339
340                         ply->elem.push_back(elem);
341
342                 } else if(!strcmp(field, "end_header")) {
343                         if(!vertex_ok || !face_ok) {
344                                 error("ply(%s): some important element is unspecified (vertex or face list)", ply_filename);
345                                 delete ply;
346                                 return 0;
347                         }
348                         ply->header_skip = ftell(fp);
349                         break;
350                 }
351         }
352
353         return ply;
354 }
355
356 static Element *seek_elem(Ply *ply, ElementType elem_type) {
357         fseek(ply->fp, ply->header_skip, SEEK_SET);
358         
359         if(ply->fmt == PLY_ASCII) {
360                 char buf[BUFFER_SIZE];
361
362                 for(size_t i=0; i<ply->elem.size(); i++) {
363                         if(ply->elem[i].type == elem_type) {
364                                 return &ply->elem[i];
365                         }
366
367                         // it's not the one we want, skip it.
368                         for(size_t j=0; j<ply->elem[i].count; j++) {
369                                 fgets(buf, BUFFER_SIZE, ply->fp);
370                         }
371                 }
372                 return 0;
373
374         } else {
375                 error("ply(%s): seek failed, binary ply loading not implemented yet", ply_filename);
376                 return 0;
377         }
378 }