eqemu
view src/objfile.cc @ 5:9b5bb05ae53a
foo
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Fri, 18 Jul 2014 00:42:15 +0300 |
parents | 3d3656360a82 |
children | 977bc1cb055b |
line source
1 #include <stdlib.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <limits.h>
5 #include <assert.h>
6 #include <vector>
7 #include <map>
8 #include <string>
9 #include <sstream>
10 #include <iomanip>
11 #include "scene.h"
13 using namespace std;
15 #define COMMANDS \
16 CMD(V), \
17 CMD(VN), \
18 CMD(VT), \
19 CMD(F), \
20 CMD(O), \
21 CMD(G), \
22 CMD(MTLLIB), \
23 CMD(USEMTL), \
24 CMD(NEWMTL), \
25 CMD(KA), \
26 CMD(KD), \
27 CMD(KS), \
28 CMD(NS), \
29 CMD(NI), \
30 CMD(D), \
31 CMD(TR), \
32 CMD(MAP_KD), \
33 CMD(MAP_KS), \
34 CMD(MAP_NS), \
35 CMD(MAP_D), \
36 CMD(REFL), \
37 CMD(BUMP)
39 #define CMD(x) CMD_##x
40 enum {
41 COMMANDS,
42 CMD_UNK
43 };
44 #undef CMD
46 #define CMD(x) #x
47 static const char *cmd_names[] = {
48 COMMANDS,
49 0
50 };
51 #undef CMD
54 struct ObjFace {
55 int elem;
56 int v[4], n[4], t[4];
57 };
59 struct ObjFile {
60 string cur_obj, cur_mat;
61 vector<Vector3> v, vn;
62 vector<Vector2> vt;
63 vector<ObjFace> f;
64 };
66 typedef Vector3 Color;
68 struct ObjMat {
69 string name; // newmtl <name>
70 Color ambient, diffuse, specular; // Ka, Kd, Ks
71 float shininess; // Ns
72 float ior; // Ni
73 float alpha; // d, Tr
75 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
76 string tex_refl; // refl -type sphere|cube file
77 string tex_bump; // bump
79 ObjMat() { reset(); }
81 void reset() {
82 ambient = diffuse = Color(0.5, 0.5, 0.5);
83 specular = Color(0.0, 0.0, 0.0);
84 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
85 shininess = 0;
86 ior = alpha = 1;
87 }
88 };
90 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl);
91 static Object *cons_object(ObjFile *obj);
93 static int get_cmd(char *str);
94 static bool is_int(const char *str);
95 static bool is_float(const char *str);
96 static bool parse_vec(Vector3 *vec);
97 static bool parse_color(Color *col);
98 static bool parse_face(ObjFace *face);
99 static const char *parse_map();
102 static map<string, Material> matlib;
105 #define INVALID_IDX INT_MIN
107 #define SEP " \t\n\r\v"
108 #define BUF_SZ 512
109 bool Scene::load_obj(FILE *fp)
110 {
111 static int seq;
112 char cur_name[16];
113 stringstream sstr;
115 ObjFile obj;
117 sprintf(cur_name, "default%02d.obj", seq++);
118 obj.cur_obj = cur_name;
120 int prev_cmd = 0, obj_added = 0;
121 for(;;) {
122 Vector3 vec;
123 ObjFace face;
125 char line[BUF_SZ];
126 fgets(line, sizeof line, fp);
127 if(feof(fp)) {
128 break;
129 }
131 char *tok;
132 if(!(tok = strtok(line, SEP))) {
133 continue; // ignore empty lines
134 }
136 int cmd;
137 if((cmd = get_cmd(tok)) == -1) {
138 continue; // ignore unknown commands ...
139 }
141 switch(cmd) {
142 case CMD_V:
143 if(!parse_vec(&vec)) {
144 continue;
145 }
146 obj.v.push_back(vec);
147 break;
149 case CMD_VN:
150 if(!parse_vec(&vec)) {
151 continue;
152 }
153 obj.vn.push_back(vec);
154 break;
156 case CMD_VT:
157 if(!parse_vec(&vec)) {
158 continue;
159 }
160 vec.y = 1.0 - vec.y;
161 obj.vt.push_back(Vector2(vec.x, vec.y));
162 break;
164 case CMD_O:
165 case CMD_G:
166 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
167 break; // just in case we've got both of them in a row
168 }
169 /* if we have any previous data, group them up, add the object
170 * and continue with the new one...
171 */
172 if(!obj.f.empty()) {
173 Object *robj = cons_object(&obj);
174 robj->mtl = matlib[obj.cur_mat];
175 add_object(robj);
176 obj_added++;
178 obj.f.clear(); // clean the face list
179 }
180 if((tok = strtok(0, SEP))) {
181 obj.cur_obj = tok;
182 } else {
183 sprintf(cur_name, "default%02d.obj", seq++);
184 obj.cur_obj = cur_name;
185 }
186 break;
188 case CMD_MTLLIB:
189 if((tok = strtok(0, SEP))) {
190 FILE *mfile;
191 if(!(mfile = fopen(tok, "rb"))) {
192 fprintf(stderr, "failed to open material library: %s\n", tok);
193 continue;
194 }
196 // load all materials of the mtl file into a vector
197 vector<ObjMat> vmtl;
198 if(!read_materials(mfile, &vmtl)) {
199 continue;
200 }
201 fclose(mfile);
203 // and add them all to the scene
204 for(size_t i=0; i<vmtl.size(); i++) {
205 Material mat;
206 mat.ambient = vmtl[i].ambient;
207 mat.diffuse = vmtl[i].diffuse;
208 mat.specular = vmtl[i].specular;
209 mat.shininess = vmtl[i].shininess;
210 mat.alpha = vmtl[i].alpha;
212 if(vmtl[i].tex_dif.length()) {
213 mat.tex[TEX_DIFFUSE] = load_texture(vmtl[i].tex_dif.c_str());
214 }
215 if(vmtl[i].tex_refl.length()) {
216 mat.tex[TEX_ENVMAP] = load_texture(vmtl[i].tex_refl.c_str());
217 }
219 matlib[vmtl[i].name] = mat;
220 }
221 }
222 break;
224 case CMD_USEMTL:
225 if((tok = strtok(0, SEP))) {
226 obj.cur_mat = tok;
227 } else {
228 obj.cur_mat = "";
229 }
230 break;
232 case CMD_F:
233 if(!parse_face(&face)) {
234 continue;
235 }
237 // convert negative indices to regular indices
238 for(int i=0; i<4; i++) {
239 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
240 face.v[i] = obj.v.size() + face.v[i];
241 }
242 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
243 face.n[i] = obj.vn.size() + face.n[i];
244 }
245 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
246 face.t[i] = obj.vt.size() + face.t[i];
247 }
248 }
250 // break quads into triangles if needed
251 obj.f.push_back(face);
252 if(face.elem == 4) {
253 face.v[1] = face.v[2];
254 face.n[1] = face.n[2];
255 face.t[1] = face.t[2];
257 face.v[2] = face.v[3];
258 face.n[2] = face.n[3];
259 face.t[2] = face.t[3];
261 obj.f.push_back(face);
262 }
263 break;
265 default:
266 break; // ignore unknown commands
267 }
269 prev_cmd = cmd;
270 }
272 // reached end of file...
273 if(!obj.f.empty()) {
274 Object *robj = cons_object(&obj);
275 robj->mtl = matlib[obj.cur_mat];
276 add_object(robj);
277 obj_added++;
278 }
280 return obj_added > 0;
281 }
283 static Object *cons_object(ObjFile *obj)
284 {
285 Object *robj;
286 Vector3 *varr, *narr;
287 Vector2 *tarr;
289 int nelem = obj->f.size() * 3;
291 assert(sizeof(Vector3) == 3 * sizeof(float));
292 assert(sizeof(Vector2) == 2 * sizeof(float));
294 try {
295 robj = new Object;
296 varr = new Vector3[nelem];
297 narr = new Vector3[nelem];
298 tarr = new Vector2[nelem];
299 }
300 catch(...) {
301 return 0;
302 }
303 if(obj->cur_obj.length() > 0) {
304 robj->set_name(obj->cur_obj.c_str());
305 }
307 // need at least one of each element
308 bool added_norm = false, added_tc = false;
309 if(obj->vn.empty()) {
310 obj->vn.push_back(Vector3(0, 0, 0));
311 added_norm = true;
312 }
313 if(obj->vt.empty()) {
314 obj->vt.push_back(Vector2(0, 0));
315 added_tc = true;
316 }
318 for(size_t i=0; i<obj->f.size(); i++) {
319 for(int j=0; j<3; j++) {
320 int idx = i * 3 + j;
321 ObjFace *f = &obj->f[i];
323 varr[idx] = obj->v[f->v[j]];
324 narr[idx] = obj->vn[f->n[j] < 0 ? 0 : f->n[j]];
326 float t = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].x;
327 float s = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].y;
328 tarr[idx] = Vector2(t, s);
329 }
330 }
332 if(added_norm) {
333 obj->vn.pop_back();
334 }
335 if(added_tc) {
336 obj->vt.pop_back();
337 }
339 Mesh *mesh = new Mesh;
340 mesh->set_attrib(MESH_ATTR_VERTEX, nelem, 3, &varr->x);
341 mesh->set_attrib(MESH_ATTR_NORMAL, nelem, 3, &narr->x);
342 mesh->set_attrib(MESH_ATTR_TEXCOORD, nelem, 2, &tarr->x);
343 robj->set_mesh(mesh);
345 printf("loaded object %s: %d faces\n", obj->cur_obj.c_str(), nelem / 3);
347 delete [] varr;
348 delete [] narr;
349 delete [] tarr;
350 return robj;
351 }
353 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl)
354 {
355 ObjMat mat;
357 for(;;) {
358 char line[BUF_SZ];
359 fgets(line, sizeof line, fp);
360 if(feof(fp)) {
361 break;
362 }
364 char *tok;
365 if(!(tok = strtok(line, SEP))) {
366 continue;
367 }
369 int cmd;
370 if((cmd = get_cmd(tok)) == -1) {
371 continue;
372 }
374 switch(cmd) {
375 case CMD_NEWMTL:
376 // add the previous material, and start a new one
377 if(mat.name.length() > 0) {
378 vmtl->push_back(mat);
379 mat.reset();
380 }
381 if((tok = strtok(0, SEP))) {
382 mat.name = tok;
383 }
384 break;
386 case CMD_KA:
387 parse_color(&mat.ambient);
388 break;
390 case CMD_KD:
391 parse_color(&mat.diffuse);
392 break;
394 case CMD_KS:
395 parse_color(&mat.specular);
396 break;
398 case CMD_NS:
399 if((tok = strtok(0, SEP)) && is_float(tok)) {
400 mat.shininess = atof(tok);
401 }
402 break;
404 case CMD_NI:
405 if((tok = strtok(0, SEP)) && is_float(tok)) {
406 mat.ior = atof(tok);
407 }
408 break;
410 case CMD_D:
411 case CMD_TR:
412 {
413 Color c;
414 if(parse_color(&c)) {
415 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
416 }
417 }
418 break;
420 case CMD_MAP_KD:
421 mat.tex_dif = parse_map();
422 break;
424 default:
425 break;
426 }
427 }
429 if(mat.name.length() > 0) {
430 vmtl->push_back(mat);
431 }
432 return true;
433 }
435 static int get_cmd(char *str)
436 {
437 char *s = str;
438 while((*s = toupper(*s))) s++;
440 for(int i=0; cmd_names[i]; i++) {
441 if(strcmp(str, cmd_names[i]) == 0) {
442 return i;
443 }
444 }
445 return CMD_UNK;
446 }
448 static bool is_int(const char *str)
449 {
450 char *tmp;
451 strtol(str, &tmp, 10);
452 return tmp != str;
453 }
455 static bool is_float(const char *str)
456 {
457 char *tmp;
458 strtod(str, &tmp);
459 return tmp != str;
460 }
462 static bool parse_vec(Vector3 *vec)
463 {
464 for(int i=0; i<3; i++) {
465 char *tok;
467 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
468 if(i < 2) {
469 return false;
470 }
471 vec->z = 0.0;
472 } else {
473 (*vec)[i] = atof(tok);
474 }
475 }
476 return true;
477 }
479 static bool parse_color(Color *col)
480 {
481 for(int i=0; i<3; i++) {
482 char *tok;
484 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
485 col->y = col->z = col->x;
486 return i > 0 ? true : false;
487 }
488 (*col)[i] = atof(tok);
489 }
490 return true;
491 }
493 static bool parse_face(ObjFace *face)
494 {
495 char *tok[] = {0, 0, 0, 0};
496 face->elem = 0;
498 for(int i=0; i<4; i++) {
499 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
500 if(i < 3) return false; // less than 3 verts? not a polygon
501 } else {
502 face->elem++;
503 }
504 }
506 for(int i=0; i<4; i++) {
507 char *subtok = tok[i];
509 if(!subtok || !*subtok || !is_int(subtok)) {
510 if(i < 3) {
511 return false;
512 }
513 face->v[i] = INVALID_IDX;
514 } else {
515 face->v[i] = atoi(subtok);
516 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
517 }
519 while(subtok && *subtok && *subtok != '/') {
520 subtok++;
521 }
522 if(subtok && *subtok && *++subtok && is_int(subtok)) {
523 face->t[i] = atoi(subtok);
524 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
525 } else {
526 face->t[i] = INVALID_IDX;
527 }
529 while(subtok && *subtok && *subtok != '/') {
530 subtok++;
531 }
532 if(subtok && *subtok && *++subtok && is_int(subtok)) {
533 face->n[i] = atoi(subtok);
534 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
535 } else {
536 face->n[i] = INVALID_IDX;
537 }
538 }
540 return true;
541 }
543 static const char *parse_map()
544 {
545 char *tok, *prev = 0;
547 while((tok = strtok(0, SEP))) {
548 prev = tok;
549 }
551 return prev ? prev : "";
552 }