eqemu

view src/objfile.cc @ 3:f9274bebe55e

adding 3d graphics stuff
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 17 Jul 2014 02:35:19 +0300
parents
children 3d3656360a82
line source
1 #include <stdlib.h>
2 #include <string.h>
3 #include <ctype.h>
4 #include <limits.h>
5 #include <vector>
6 #include <map>
7 #include <string>
8 #include <sstream>
9 #include <iomanip>
10 #include "scene.h"
12 using namespace std;
14 #define COMMANDS \
15 CMD(V), \
16 CMD(VN), \
17 CMD(VT), \
18 CMD(F), \
19 CMD(O), \
20 CMD(G), \
21 CMD(MTLLIB), \
22 CMD(USEMTL), \
23 CMD(NEWMTL), \
24 CMD(KA), \
25 CMD(KD), \
26 CMD(KS), \
27 CMD(NS), \
28 CMD(NI), \
29 CMD(D), \
30 CMD(TR), \
31 CMD(MAP_KD), \
32 CMD(MAP_KS), \
33 CMD(MAP_NS), \
34 CMD(MAP_D), \
35 CMD(REFL), \
36 CMD(BUMP)
38 #define CMD(x) CMD_##x
39 enum {
40 COMMANDS,
41 CMD_UNK
42 };
43 #undef CMD
45 #define CMD(x) #x
46 static const char *cmd_names[] = {
47 COMMANDS,
48 0
49 };
50 #undef CMD
53 struct ObjFace {
54 int elem;
55 int v[4], n[4], t[4];
56 };
58 struct ObjFile {
59 string cur_obj, cur_mat;
60 vector<Vector3> v, vn;
61 vector<Vector2> vt;
62 vector<ObjFace> f;
63 };
65 typedef Vector3 Color;
67 struct ObjMat {
68 string name; // newmtl <name>
69 Color ambient, diffuse, specular; // Ka, Kd, Ks
70 float shininess; // Ns
71 float ior; // Ni
72 float alpha; // d, Tr
74 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
75 string tex_refl; // refl -type sphere|cube file
76 string tex_bump; // bump
78 ObjMat() { reset(); }
80 void reset() {
81 ambient = diffuse = Color(0.5, 0.5, 0.5);
82 specular = Color(0.0, 0.0, 0.0);
83 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
84 shininess = 0;
85 ior = alpha = 1;
86 }
87 };
89 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl);
90 static Object *cons_object(ObjFile *obj);
92 static int get_cmd(char *str);
93 static bool is_int(const char *str);
94 static bool is_float(const char *str);
95 static bool parse_vec(Vector3 *vec);
96 static bool parse_color(Color *col);
97 static bool parse_face(ObjFace *face);
98 static const char *parse_map();
101 static map<string, Material> matlib;
104 #define INVALID_IDX INT_MIN
106 #define SEP " \t\n\r\v"
107 #define BUF_SZ 512
108 bool Scene::load_obj(FILE *fp)
109 {
110 static int seq;
111 char cur_name[16];
112 stringstream sstr;
114 ObjFile obj;
116 sprintf(cur_name, "default%02d.obj", seq++);
117 obj.cur_obj = cur_name;
119 int prev_cmd = 0, obj_added = 0;
120 for(;;) {
121 Vector3 vec;
122 ObjFace face;
124 char line[BUF_SZ];
125 fgets(line, sizeof line, fp);
126 if(feof(fp)) {
127 break;
128 }
130 char *tok;
131 if(!(tok = strtok(line, SEP))) {
132 continue; // ignore empty lines
133 }
135 int cmd;
136 if((cmd = get_cmd(tok)) == -1) {
137 continue; // ignore unknown commands ...
138 }
140 switch(cmd) {
141 case CMD_V:
142 if(!parse_vec(&vec)) {
143 continue;
144 }
145 obj.v.push_back(vec);
146 break;
148 case CMD_VN:
149 if(!parse_vec(&vec)) {
150 continue;
151 }
152 obj.vn.push_back(vec);
153 break;
155 case CMD_VT:
156 if(!parse_vec(&vec)) {
157 continue;
158 }
159 vec.y = 1.0 - vec.y;
160 obj.vt.push_back(Vector2(vec.x, vec.y));
161 break;
163 case CMD_O:
164 case CMD_G:
165 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
166 break; // just in case we've got both of them in a row
167 }
168 /* if we have any previous data, group them up, add the object
169 * and continue with the new one...
170 */
171 if(!obj.f.empty()) {
172 Object *robj = cons_object(&obj);
173 robj->mtl = matlib[obj.cur_mat];
174 add_object(robj);
175 obj_added++;
177 obj.f.clear(); // clean the face list
178 }
179 if((tok = strtok(0, SEP))) {
180 obj.cur_obj = tok;
181 } else {
182 sprintf(cur_name, "default%02d.obj", seq++);
183 obj.cur_obj = cur_name;
184 }
185 break;
187 case CMD_MTLLIB:
188 if((tok = strtok(0, SEP))) {
189 FILE *mfile;
190 if(!(mfile = fopen(tok, "rb"))) {
191 fprintf(stderr, "failed to open material library: %s\n", tok);
192 continue;
193 }
195 // load all materials of the mtl file into a vector
196 vector<ObjMat> vmtl;
197 if(!read_materials(mfile, &vmtl)) {
198 continue;
199 }
200 fclose(mfile);
202 // and add them all to the scene
203 for(size_t i=0; i<vmtl.size(); i++) {
204 Material mat;
205 mat.ambient = vmtl[i].ambient;
206 mat.diffuse = vmtl[i].diffuse;
207 mat.specular = vmtl[i].specular;
208 mat.shininess = vmtl[i].shininess;
209 mat.alpha = vmtl[i].alpha;
211 /*if(vmtl[i].tex_dif.length()) {
212 mat.set_texture(get_texture(vmtl[i].tex_dif.c_str()));
213 }*/
215 matlib[vmtl[i].name] = mat;
216 }
217 }
218 break;
220 case CMD_USEMTL:
221 if((tok = strtok(0, SEP))) {
222 obj.cur_mat = tok;
223 } else {
224 obj.cur_mat = "";
225 }
226 break;
228 case CMD_F:
229 if(!parse_face(&face)) {
230 continue;
231 }
233 // convert negative indices to regular indices
234 for(int i=0; i<4; i++) {
235 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
236 face.v[i] = obj.v.size() + face.v[i];
237 }
238 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
239 face.n[i] = obj.vn.size() + face.n[i];
240 }
241 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
242 face.t[i] = obj.vt.size() + face.t[i];
243 }
244 }
246 // break quads into triangles if needed
247 obj.f.push_back(face);
248 if(face.elem == 4) {
249 face.v[1] = face.v[2];
250 face.n[1] = face.n[2];
251 face.t[1] = face.t[2];
253 face.v[2] = face.v[3];
254 face.n[2] = face.n[3];
255 face.t[2] = face.t[3];
257 obj.f.push_back(face);
258 }
259 break;
261 default:
262 break; // ignore unknown commands
263 }
265 prev_cmd = cmd;
266 }
268 // reached end of file...
269 if(!obj.f.empty()) {
270 Object *robj = cons_object(&obj);
271 robj->mtl = matlib[obj.cur_mat];
272 add_object(robj);
273 obj_added++;
274 }
276 return obj_added > 0;
277 }
279 static Object *cons_object(ObjFile *obj)
280 {
281 Object *robj;
282 Vector3 *varr, *narr;
283 Vector2 *tarr;
285 int nelem = obj->f.size() * 3;
287 try {
288 robj = new Object;
289 varr = new Vector3[nelem];
290 narr = new Vector3[nelem];
291 tarr = new Vector2[nelem];
292 }
293 catch(...) {
294 return 0;
295 }
296 /*if(obj->cur_obj.length() > 0) {
297 robj->set_name(obj->cur_obj.c_str());
298 }*/
300 // need at least one of each element
301 bool added_norm = false, added_tc = false;
302 if(obj->vn.empty()) {
303 obj->vn.push_back(Vector3(0, 0, 0));
304 added_norm = true;;
305 }
306 if(obj->vt.empty()) {
307 obj->vt.push_back(Vector2(0, 0));
308 added_tc = true;
309 }
311 for(size_t i=0; i<obj->f.size(); i++) {
312 for(int j=0; j<3; j++) {
313 int idx = i * 3 + j;
314 ObjFace *f = &obj->f[i];
316 varr[idx] = obj->v[f->v[j]];
317 narr[idx] = obj->vn[f->n[j] < 0 ? 0 : f->n[j]];
319 float t = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].x;
320 float s = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].y;
321 tarr[idx] = Vector2(t, s);
322 }
323 }
325 if(added_norm) {
326 obj->vn.pop_back();
327 }
328 if(added_tc) {
329 obj->vt.pop_back();
330 }
332 Mesh *mesh = robj->get_mesh();
333 mesh->set_attrib(MESH_ATTR_VERTEX, nelem, 3, &varr->x);
334 mesh->set_attrib(MESH_ATTR_NORMAL, nelem, 3, &narr->x);
335 mesh->set_attrib(MESH_ATTR_TEXCOORD, nelem, 2, &tarr->x);
337 delete [] varr;
338 delete [] narr;
339 delete [] tarr;
340 return robj;
341 }
343 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl)
344 {
345 ObjMat mat;
347 for(;;) {
348 char line[BUF_SZ];
349 fgets(line, sizeof line, fp);
350 if(feof(fp)) {
351 break;
352 }
354 char *tok;
355 if(!(tok = strtok(line, SEP))) {
356 continue;
357 }
359 int cmd;
360 if((cmd = get_cmd(tok)) == -1) {
361 continue;
362 }
364 switch(cmd) {
365 case CMD_NEWMTL:
366 // add the previous material, and start a new one
367 if(mat.name.length() > 0) {
368 vmtl->push_back(mat);
369 mat.reset();
370 }
371 if((tok = strtok(0, SEP))) {
372 mat.name = tok;
373 }
374 break;
376 case CMD_KA:
377 parse_color(&mat.ambient);
378 break;
380 case CMD_KD:
381 parse_color(&mat.diffuse);
382 break;
384 case CMD_KS:
385 parse_color(&mat.specular);
386 break;
388 case CMD_NS:
389 if((tok = strtok(0, SEP)) && is_float(tok)) {
390 mat.shininess = atof(tok);
391 }
392 break;
394 case CMD_NI:
395 if((tok = strtok(0, SEP)) && is_float(tok)) {
396 mat.ior = atof(tok);
397 }
398 break;
400 case CMD_D:
401 case CMD_TR:
402 {
403 Color c;
404 if(parse_color(&c)) {
405 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
406 }
407 }
408 break;
410 case CMD_MAP_KD:
411 mat.tex_dif = parse_map();
412 break;
414 default:
415 break;
416 }
417 }
419 if(mat.name.length() > 0) {
420 vmtl->push_back(mat);
421 }
422 return true;
423 }
425 static int get_cmd(char *str)
426 {
427 char *s = str;
428 while((*s = toupper(*s))) s++;
430 for(int i=0; cmd_names[i]; i++) {
431 if(strcmp(str, cmd_names[i]) == 0) {
432 return i;
433 }
434 }
435 return CMD_UNK;
436 }
438 static bool is_int(const char *str)
439 {
440 char *tmp;
441 strtol(str, &tmp, 10);
442 return tmp != str;
443 }
445 static bool is_float(const char *str)
446 {
447 char *tmp;
448 strtod(str, &tmp);
449 return tmp != str;
450 }
452 static bool parse_vec(Vector3 *vec)
453 {
454 for(int i=0; i<3; i++) {
455 char *tok;
457 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
458 if(i < 2) {
459 return false;
460 }
461 vec->z = 0.0;
462 } else {
463 (*vec)[i] = atof(tok);
464 }
465 }
466 return true;
467 }
469 static bool parse_color(Color *col)
470 {
471 for(int i=0; i<3; i++) {
472 char *tok;
474 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
475 col->y = col->z = col->x;
476 return i > 0 ? true : false;
477 }
478 (*col)[i] = atof(tok);
479 }
480 return true;
481 }
483 static bool parse_face(ObjFace *face)
484 {
485 char *tok[] = {0, 0, 0, 0};
486 face->elem = 0;
488 for(int i=0; i<4; i++) {
489 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
490 if(i < 3) return false; // less than 3 verts? not a polygon
491 } else {
492 face->elem++;
493 }
494 }
496 for(int i=0; i<4; i++) {
497 char *subtok = tok[i];
499 if(!subtok || !*subtok || !is_int(subtok)) {
500 if(i < 3) {
501 return false;
502 }
503 face->v[i] = INVALID_IDX;
504 } else {
505 face->v[i] = atoi(subtok);
506 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
507 }
509 while(subtok && *subtok && *subtok != '/') {
510 subtok++;
511 }
512 if(subtok && *subtok && *++subtok && is_int(subtok)) {
513 face->t[i] = atoi(subtok);
514 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
515 } else {
516 face->t[i] = INVALID_IDX;
517 }
519 while(subtok && *subtok && *subtok != '/') {
520 subtok++;
521 }
522 if(subtok && *subtok && *++subtok && is_int(subtok)) {
523 face->n[i] = atoi(subtok);
524 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
525 } else {
526 face->n[i] = INVALID_IDX;
527 }
528 }
530 return true;
531 }
533 static const char *parse_map()
534 {
535 char *tok, *prev = 0;
537 while((tok = strtok(0, SEP))) {
538 prev = tok;
539 }
541 return prev ? prev : "";
542 }