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