coeng

view src/objfile.c @ 8:8cce82794f90

seems to work nicely
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 15 Feb 2015 05:14:20 +0200
parents
children
line source
1 /* version 2: does not crash when there are no texture coordinates or normals */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <limits.h>
7 #include <ctype.h>
8 #include "objfile.h"
10 #define INVALID_IDX INT_MIN
12 struct objfile {
13 float *verts, *normals, *texcoords;
14 int num_faces;
15 };
17 struct vec3 {
18 float x, y, z;
19 };
21 struct face {
22 int vidx[4];
23 int nidx[4];
24 int tidx[4];
25 };
27 static int objf_read(struct objfile *obj, FILE *fp, const char *fname);
28 static int count_elem(FILE *fp, const char *elem);
29 static char *strip(char *s);
30 static int mkface(char *line, struct face *face);
31 static int parse_face(char *s, int *vidx, int *nidx, int *tidx);
32 static int is_int(const char *str);
35 struct objfile *objf_load(const char *fname)
36 {
37 FILE *fp;
38 struct objfile *obj;
40 if(!(fp = fopen(fname, "rb"))) {
41 fprintf(stderr, "objf_load: failed to open file: %s: %s\n", fname, strerror(errno));
42 return 0;
43 }
45 if(!(obj = malloc(sizeof *obj))) {
46 perror("failed to allocate objfile structure\n");
47 fclose(fp);
48 return 0;
49 }
51 if(objf_read(obj, fp, fname) == -1) {
52 free(obj);
53 obj = 0;
54 }
55 fclose(fp);
56 return obj;
57 }
59 void objf_free(struct objfile *obj)
60 {
61 if(obj) {
62 free(obj->verts);
63 free(obj->normals);
64 free(obj->texcoords);
65 free(obj);
66 }
67 }
69 int objf_vertex_count(struct objfile *obj)
70 {
71 return obj->num_faces * 3;
72 }
74 int objf_face_count(struct objfile *obj)
75 {
76 return obj->num_faces;
77 }
79 float *objf_vertices(struct objfile *obj)
80 {
81 return obj->verts;
82 }
84 float *objf_normals(struct objfile *obj)
85 {
86 return obj->normals;
87 }
89 float *objf_texcoords(struct objfile *obj)
90 {
91 return obj->texcoords;
92 }
95 float *objf_vertex(struct objfile *obj, int idx)
96 {
97 return obj->verts + idx * 3;
98 }
100 float *objf_normal(struct objfile *obj, int idx)
101 {
102 return obj->normals + idx * 3;
103 }
105 float *objf_texcoord(struct objfile *obj, int idx)
106 {
107 return obj->texcoords + idx * 2;
108 }
110 static int objf_read(struct objfile *obj, FILE *fp, const char *fname)
111 {
112 int i, j, res = -1;
113 int vcount, ncount, tcount, fcount;
114 int num_verts;
115 struct vec3 *varr, *narr, *tarr;
116 struct vec3 *vptr, *nptr, *tptr;
117 struct vec3 dummy_vec = {0, 0, 0};
118 struct face *faces, *fptr;
119 float *vdest, *ndest, *tdest;
120 char buf[512];
122 vcount = count_elem(fp, "v");
123 rewind(fp);
124 ncount = count_elem(fp, "vn");
125 rewind(fp);
126 tcount = count_elem(fp, "vt");
127 rewind(fp);
128 fcount = count_elem(fp, "f");
129 rewind(fp);
131 obj->num_faces = fcount;
133 if(!vcount || !fcount) {
134 fprintf(stderr, "invalid or corrupted obj file: %s\n", fname);
135 return -1;
136 }
138 printf("Reading obj file: %s (%d vertices, %d faces)\n", fname, vcount, fcount);
140 vptr = varr = malloc(vcount * sizeof *varr);
141 nptr = narr = ncount ? malloc(ncount * sizeof *narr) : &dummy_vec;
142 tptr = tarr = tcount ? malloc(tcount * sizeof *tarr) : &dummy_vec;
143 fptr = faces = malloc(fcount * sizeof *faces);
145 if(!varr || !narr || !tarr || !faces) {
146 perror("can't allocate enough memory for the geometry");
147 goto cleanup;
148 }
150 while(fgets(buf, sizeof buf, fp)) {
151 char *line;
153 if(!(line = strip(buf)) || *line == '#') {
154 continue; /* ignore empty lines and comments */
155 }
157 if(sscanf(line, "v %f %f %f", &vptr->x, &vptr->y, &vptr->z) == 3) {
158 vptr++;
159 } else if(sscanf(line, "vn %f %f %f", &nptr->x, &nptr->y, &nptr->z) == 3) {
160 nptr++;
161 } else if(sscanf(line, "vt %f %f", &tptr->x, &tptr->y) == 2) {
162 tptr++;
163 } else if(mkface(line, fptr) == 0) {
164 fptr++;
165 }
166 }
168 /* now go forth and create the straight-up 3-vert-per-face vertex arrays */
169 num_verts = obj->num_faces * 3;
171 vdest = obj->verts = malloc(num_verts * 3 * sizeof *obj->verts);
172 ndest = obj->normals = malloc(num_verts * 3 * sizeof *obj->normals);
173 tdest = obj->texcoords = malloc(num_verts * 2 * sizeof *obj->texcoords);
175 if(!obj->verts || !obj->normals || !obj->texcoords) {
176 free(obj->verts);
177 free(obj->normals);
178 goto cleanup;
179 }
181 /* for all faces */
182 for(i=0; i<fcount; i++) {
183 /* for the 3 vertices of each face */
184 for(j=0; j<3; j++) {
185 *vdest++ = varr[faces[i].vidx[j]].x;
186 *vdest++ = varr[faces[i].vidx[j]].y;
187 *vdest++ = varr[faces[i].vidx[j]].z;
189 *ndest++ = narr[faces[i].nidx[j]].x;
190 *ndest++ = narr[faces[i].nidx[j]].y;
191 *ndest++ = narr[faces[i].nidx[j]].z;
193 *tdest++ = tarr[faces[i].tidx[j]].x;
194 *tdest++ = tarr[faces[i].tidx[j]].y;
195 }
196 }
197 res = 0; /* success */
199 cleanup:
200 free(varr);
201 if(narr != &dummy_vec) {
202 free(narr);
203 }
204 if(tarr != &dummy_vec) {
205 free(tarr);
206 }
207 free(faces);
208 return res;
209 }
211 static int count_elem(FILE *fp, const char *elem)
212 {
213 int count = 0;
214 char buf[512];
216 while(fgets(buf, sizeof buf, fp)) {
217 if(buf[0] == elem[0] && (!elem[1] || buf[1] == elem[1])) {
218 count++;
219 }
220 }
221 return count;
222 }
225 static char *strip(char *s)
226 {
227 while(*s && isspace(*s)) {
228 s++;
229 }
230 return s;
231 }
233 static int mkface(char *s, struct face *face)
234 {
235 int nverts;
237 if(s[0] != 'f') {
238 return -1;
239 }
241 if((nverts = parse_face(s + 2, face->vidx, face->nidx, face->tidx)) == -1) {
242 return -1;
243 }
244 if(nverts > 3) {
245 fprintf(stderr, "warning face with more than 3 vertices found\n");
246 }
247 return 0;
248 }
250 #define SEP " \t\v\n\r"
251 /* returns the number of vertices on this face, or -1 on error. */
252 static int parse_face(char *s, int *vidx, int *nidx, int *tidx)
253 {
254 int i, num_verts = 0;
255 char *tok[] = {0, 0, 0, 0};
256 char *subtok;
258 for(i=0; i<4; i++) {
259 if((!(tok[i] = strtok(i == 0 ? s : 0, SEP)) || !is_int(tok[i]))) {
260 if(i < 3) return -1;
261 } else {
262 num_verts++;
263 }
264 }
266 for(i=0; i<4; i++) {
267 subtok = tok[i];
268 if(!subtok || !*subtok || !is_int(subtok)) {
269 if(i < 3) {
270 return -1;
271 }
272 vidx[i] = INVALID_IDX;
273 } else {
274 vidx[i] = atoi(subtok);
275 if(vidx[i] > 0) vidx[i]--; /* convert to 0-based */
276 }
278 while(subtok && *subtok && *subtok != '/') {
279 subtok++;
280 }
281 if(subtok && *subtok && *++subtok && is_int(subtok)) {
282 tidx[i] = atoi(subtok);
283 if(tidx[i] > 0) tidx[i]--; /* convert to 0-based */
284 } else {
285 tidx[i] = 0;
286 }
288 while(subtok && *subtok && *subtok != '/') {
289 subtok++;
290 }
291 if(subtok && *subtok && *++subtok && is_int(subtok)) {
292 nidx[i] = atoi(subtok);
293 if(nidx[i] > 0) nidx[i]--; /* convert to 0-based */
294 } else {
295 nidx[i] = 0;
296 }
297 }
299 return num_verts;
300 }
303 static int is_int(const char *str)
304 {
305 return isdigit(str[0]) ||
306 (str[0] == '-' && isdigit(str[1])) ||
307 (str[0] == '+' && isdigit(str[1]));
308 }