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