rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <string.h>
|
nuclear@0
|
3 #include <ctype.h>
|
nuclear@0
|
4 #include <limits.h>
|
nuclear@0
|
5 #include <errno.h>
|
nuclear@0
|
6 #include <vector>
|
nuclear@0
|
7 #include "mesh.h"
|
nuclear@0
|
8 #include "scene.h"
|
nuclear@0
|
9 #include "scnload.h"
|
nuclear@0
|
10
|
nuclear@0
|
11 enum {
|
nuclear@0
|
12 CMD_VERTEX,
|
nuclear@0
|
13 CMD_NORMAL,
|
nuclear@0
|
14 CMD_TEXCOORD,
|
nuclear@0
|
15 CMD_FACE,
|
nuclear@0
|
16 CMD_OBJECT,
|
nuclear@0
|
17 CMD_GROUP,
|
nuclear@0
|
18 CMD_MTLLIB
|
nuclear@0
|
19 };
|
nuclear@0
|
20
|
nuclear@0
|
21 static struct {
|
nuclear@0
|
22 const char *s;
|
nuclear@0
|
23 int cmd;
|
nuclear@0
|
24 } commands[] = {
|
nuclear@0
|
25 {"v", CMD_VERTEX},
|
nuclear@0
|
26 {"vn", CMD_NORMAL},
|
nuclear@0
|
27 {"vt", CMD_TEXCOORD},
|
nuclear@0
|
28 {"f", CMD_FACE},
|
nuclear@0
|
29 {"o", CMD_OBJECT},
|
nuclear@0
|
30 {"g", CMD_GROUP},
|
nuclear@0
|
31 {"mtllib", CMD_MTLLIB},
|
nuclear@0
|
32 {0, -1}
|
nuclear@0
|
33 };
|
nuclear@0
|
34
|
nuclear@0
|
35 struct Face {
|
nuclear@0
|
36 int vnum;
|
nuclear@0
|
37 int vidx[4];
|
nuclear@0
|
38 int nidx[4];
|
nuclear@0
|
39 int tidx[4];
|
nuclear@0
|
40 };
|
nuclear@0
|
41
|
nuclear@0
|
42 struct ObjMesh {
|
nuclear@0
|
43 std::vector<Vector3> varr;
|
nuclear@0
|
44 std::vector<Vector3> narr;
|
nuclear@0
|
45 std::vector<Vector2> tarr;
|
nuclear@0
|
46 std::vector<Face> faces;
|
nuclear@0
|
47 };
|
nuclear@0
|
48
|
nuclear@0
|
49 struct ParserState {
|
nuclear@0
|
50 FILE *fp;
|
nuclear@0
|
51 Scene *scn;
|
nuclear@0
|
52 const char *fname;
|
nuclear@0
|
53
|
nuclear@0
|
54 unsigned int voffs, noffs, toffs;
|
nuclear@0
|
55 ObjMesh omesh;
|
nuclear@0
|
56 };
|
nuclear@0
|
57
|
nuclear@0
|
58 static bool parse_line(ParserState *ps, char *line);
|
nuclear@0
|
59 static void flush_mesh(ParserState *ps);
|
nuclear@0
|
60 static char *clean_input(char *s);
|
nuclear@0
|
61
|
nuclear@0
|
62 bool load_obj(Scene *scn, const char *fname)
|
nuclear@0
|
63 {
|
nuclear@0
|
64 FILE *fp;
|
nuclear@0
|
65 char buf[256];
|
nuclear@0
|
66 bool result = false;
|
nuclear@0
|
67 ParserState ps;
|
nuclear@0
|
68
|
nuclear@0
|
69 if(!(fp = fopen(fname, "r"))) {
|
nuclear@0
|
70 fprintf(stderr, "failed to open obj file: %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
71 return false;
|
nuclear@0
|
72 }
|
nuclear@0
|
73 ps.fp = fp;
|
nuclear@0
|
74 ps.scn = scn;
|
nuclear@0
|
75 ps.fname = fname;
|
nuclear@0
|
76 ps.voffs = ps.noffs = ps.toffs = 0;
|
nuclear@0
|
77
|
nuclear@0
|
78 int nline = 0;
|
nuclear@0
|
79 while(fgets(buf, sizeof buf, fp)) {
|
nuclear@0
|
80 ++nline;
|
nuclear@0
|
81 char *line = clean_input(buf);
|
nuclear@0
|
82 if(!*line) continue;
|
nuclear@0
|
83
|
nuclear@0
|
84 if(!parse_line(&ps, line)) {
|
nuclear@0
|
85 fprintf(stderr, "[obj] %s:%d: failed to parse\n", fname, nline);
|
nuclear@0
|
86 goto done;
|
nuclear@0
|
87 }
|
nuclear@0
|
88 }
|
nuclear@0
|
89 flush_mesh(&ps);
|
nuclear@0
|
90 result = true;
|
nuclear@0
|
91
|
nuclear@0
|
92 done:
|
nuclear@0
|
93 fclose(fp);
|
nuclear@0
|
94 return result;
|
nuclear@0
|
95 }
|
nuclear@0
|
96
|
nuclear@0
|
97 static int parse_cmd(char *line, char **endp)
|
nuclear@0
|
98 {
|
nuclear@0
|
99 char *end = line;
|
nuclear@0
|
100 while(*end && !isspace(*end)) end++;
|
nuclear@0
|
101 *end = 0;
|
nuclear@0
|
102 *endp = end;
|
nuclear@0
|
103
|
nuclear@0
|
104 for(int i=0; commands[i].s; i++) {
|
nuclear@0
|
105 if(strcmp(commands[i].s, line) == 0) {
|
nuclear@0
|
106 return commands[i].cmd;
|
nuclear@0
|
107 }
|
nuclear@0
|
108 }
|
nuclear@0
|
109 return -1;
|
nuclear@0
|
110 }
|
nuclear@0
|
111
|
nuclear@0
|
112 static bool parse_vec3(Vector3 *vp, char *line)
|
nuclear@0
|
113 {
|
nuclear@0
|
114 return sscanf(line, "%f %f %f", &vp->x, &vp->y, &vp->z) == 3;
|
nuclear@0
|
115 }
|
nuclear@0
|
116
|
nuclear@0
|
117 static bool parse_vec2(Vector2 *vp, char *line)
|
nuclear@0
|
118 {
|
nuclear@0
|
119 return sscanf(line, "%f %f", &vp->x, &vp->y) == 2;
|
nuclear@0
|
120 }
|
nuclear@0
|
121
|
nuclear@0
|
122 #define INVAL_IDX INT_MIN
|
nuclear@0
|
123
|
nuclear@0
|
124 static bool parse_face(Face *fp, char *line)
|
nuclear@0
|
125 {
|
nuclear@0
|
126 fp->vnum = 0;
|
nuclear@0
|
127
|
nuclear@0
|
128 int res;
|
nuclear@0
|
129 char *tok, *ptr = line;
|
nuclear@0
|
130 for(int i=0; i<4; i++) {
|
nuclear@0
|
131 while(*ptr && isspace(*ptr)) ptr++;
|
nuclear@0
|
132 if(!*ptr) break;
|
nuclear@0
|
133 tok = ptr;
|
nuclear@0
|
134
|
nuclear@0
|
135 fp->vnum++;
|
nuclear@0
|
136
|
nuclear@0
|
137 while(*ptr && !isspace(*ptr)) ptr++;
|
nuclear@0
|
138 if(*ptr) *ptr++ = 0;
|
nuclear@0
|
139
|
nuclear@0
|
140 // parse the index descriptor vidx[/[tidx]/[nidx]]
|
nuclear@0
|
141 if(sscanf(tok, "%d//%d", &fp->vidx[i], &fp->nidx[i]) == 2) {
|
nuclear@0
|
142 fp->tidx[i] = INVAL_IDX;
|
nuclear@0
|
143 continue;
|
nuclear@0
|
144 }
|
nuclear@0
|
145 res = sscanf(tok, "%d/%d/%d", &fp->vidx[i], &fp->tidx[i], &fp->nidx[i]);
|
nuclear@0
|
146 if(res > 2) {
|
nuclear@0
|
147 if(res != 3) {
|
nuclear@0
|
148 fp->nidx[i] = INVAL_IDX;
|
nuclear@0
|
149 }
|
nuclear@0
|
150 continue;
|
nuclear@0
|
151 }
|
nuclear@0
|
152 char *endp;
|
nuclear@0
|
153 fp->vidx[i] = strtol(tok, &endp, 10);
|
nuclear@0
|
154 if(endp == tok) {
|
nuclear@0
|
155 return false;
|
nuclear@0
|
156 }
|
nuclear@0
|
157 fp->nidx[i] = fp->tidx[i] = INVAL_IDX;
|
nuclear@0
|
158 }
|
nuclear@0
|
159 return fp->vnum >= 3 ? true : false;
|
nuclear@0
|
160 }
|
nuclear@0
|
161
|
nuclear@0
|
162 static bool parse_line(ParserState *ps, char *line)
|
nuclear@0
|
163 {
|
nuclear@0
|
164 char *endp;
|
nuclear@0
|
165 int cmd = parse_cmd(line, &endp);
|
nuclear@0
|
166 if(cmd == -1) return true; // ignore unknown commands
|
nuclear@0
|
167 line = endp + 1;
|
nuclear@0
|
168 while(*line && isspace(*line)) line++;
|
nuclear@0
|
169
|
nuclear@0
|
170 Vector3 v;
|
nuclear@0
|
171 Vector2 v2;
|
nuclear@0
|
172 Face f;
|
nuclear@0
|
173
|
nuclear@0
|
174 switch(cmd) {
|
nuclear@0
|
175 case CMD_VERTEX:
|
nuclear@0
|
176 if(!parse_vec3(&v, line)) return false;
|
nuclear@0
|
177 ps->omesh.varr.push_back(v);
|
nuclear@0
|
178 break;
|
nuclear@0
|
179
|
nuclear@0
|
180 case CMD_NORMAL:
|
nuclear@0
|
181 if(!parse_vec3(&v, line)) return false;
|
nuclear@0
|
182 ps->omesh.narr.push_back(v);
|
nuclear@0
|
183 break;
|
nuclear@0
|
184
|
nuclear@0
|
185 case CMD_TEXCOORD:
|
nuclear@0
|
186 if(!parse_vec2(&v2, line)) return false;
|
nuclear@0
|
187 ps->omesh.tarr.push_back(v2);
|
nuclear@0
|
188 break;
|
nuclear@0
|
189
|
nuclear@0
|
190 case CMD_FACE:
|
nuclear@0
|
191 if(!parse_face(&f, line)) return false;
|
nuclear@0
|
192 ps->omesh.faces.push_back(f);
|
nuclear@0
|
193 break;
|
nuclear@0
|
194
|
nuclear@0
|
195 case CMD_OBJECT:
|
nuclear@0
|
196 case CMD_GROUP:
|
nuclear@0
|
197 flush_mesh(ps);
|
nuclear@0
|
198 break;
|
nuclear@0
|
199
|
nuclear@0
|
200 default:
|
nuclear@0
|
201 break;
|
nuclear@0
|
202 }
|
nuclear@0
|
203 return true;
|
nuclear@0
|
204 }
|
nuclear@0
|
205
|
nuclear@0
|
206 static void flush_mesh(ParserState *ps)
|
nuclear@0
|
207 {
|
nuclear@0
|
208 if(!ps->omesh.varr.empty() && !ps->omesh.faces.empty()) {
|
nuclear@0
|
209 int num_verts = (int)ps->omesh.varr.size();
|
nuclear@0
|
210 int num_faces = (int)ps->omesh.faces.size();
|
nuclear@0
|
211
|
nuclear@0
|
212 Mesh *m = new Mesh;
|
nuclear@0
|
213 m->set_attrib_data(MESH_ATTR_VERTEX, 3, num_verts, (float*)&ps->omesh.varr[0]);
|
nuclear@0
|
214
|
nuclear@0
|
215 if(!ps->omesh.narr.empty()) {
|
nuclear@0
|
216 m->set_attrib_data(MESH_ATTR_NORMAL, 3, num_verts, (float*)&ps->omesh.narr[0]);
|
nuclear@0
|
217 }
|
nuclear@0
|
218 if(!ps->omesh.tarr.empty()) {
|
nuclear@0
|
219 m->set_attrib_data(MESH_ATTR_TEXCOORD, 2, num_verts, (float*)&ps->omesh.tarr[0]);
|
nuclear@0
|
220 }
|
nuclear@0
|
221
|
nuclear@0
|
222 unsigned int *idxarr = m->set_index_data(num_faces * 3);
|
nuclear@0
|
223 for(int i=0; i<num_faces; i++) {
|
nuclear@0
|
224 for(int j=0; j<3; j++) {
|
nuclear@0
|
225 int idx = ps->omesh.faces[i].vidx[j] - ps->voffs - 1; // TODO normalize indices
|
nuclear@0
|
226 *idxarr++ = idx;
|
nuclear@0
|
227 }
|
nuclear@0
|
228 }
|
nuclear@0
|
229
|
nuclear@0
|
230 Object *o = new Object;
|
nuclear@0
|
231 o->set_mesh(m);
|
nuclear@0
|
232
|
nuclear@0
|
233 // TODO material & textures
|
nuclear@0
|
234
|
nuclear@0
|
235 ps->scn->add_object(o);
|
nuclear@0
|
236
|
nuclear@0
|
237 printf("added object with %d vertices and %d faces\n", num_verts, num_faces);
|
nuclear@0
|
238 }
|
nuclear@0
|
239
|
nuclear@0
|
240 ps->voffs += ps->omesh.varr.size();
|
nuclear@0
|
241 ps->noffs += ps->omesh.narr.size();
|
nuclear@0
|
242 ps->toffs += ps->omesh.tarr.size();
|
nuclear@0
|
243
|
nuclear@0
|
244 ps->omesh.varr.clear();
|
nuclear@0
|
245 ps->omesh.narr.clear();
|
nuclear@0
|
246 ps->omesh.tarr.clear();
|
nuclear@0
|
247 ps->omesh.faces.clear();
|
nuclear@0
|
248 }
|
nuclear@0
|
249
|
nuclear@0
|
250 static char *clean_input(char *s)
|
nuclear@0
|
251 {
|
nuclear@0
|
252 while(*s && isspace(*s)) s++;
|
nuclear@0
|
253 if(!*s) return s;
|
nuclear@0
|
254
|
nuclear@0
|
255 char *end = strchr(s, '#');
|
nuclear@0
|
256 if(end) *end = 0;
|
nuclear@0
|
257
|
nuclear@0
|
258 end = s + strlen(s) - 1;
|
nuclear@0
|
259 while(end > s && isspace(*end)) end--;
|
nuclear@0
|
260 end[1] = 0;
|
nuclear@0
|
261
|
nuclear@0
|
262 return s;
|
nuclear@0
|
263 }
|