2 eqemu - electronic queue system emulator
3 Copyright (C) 2014 John Tsiombikas <nuclear@member.fsf.org>,
4 Eleni-Maria Stea <eleni@mutantstargoat.com>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
60 #define CMD(x) CMD_##x
68 static const char *cmd_names[] = {
81 string cur_obj, cur_mat;
82 vector<Vector3> v, vn;
87 typedef Vector3 Color;
90 string name; // newmtl <name>
91 Color ambient, diffuse, specular; // Ka, Kd, Ks
93 float shininess; // Ns
97 string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d
98 string tex_refl; // refl -type sphere|cube file
99 string tex_bump; // bump
101 ObjMat() { reset(); }
104 ambient = diffuse = Color(0.5, 0.5, 0.5);
105 specular = Color(0.0, 0.0, 0.0);
106 name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = "";
112 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl);
113 static Object *cons_object(ObjFile *obj);
115 static int get_cmd(char *str);
116 static bool is_int(const char *str);
117 static bool is_float(const char *str);
118 static bool parse_vec(Vector3 *vec);
119 static bool parse_color(Color *col);
120 static bool parse_face(ObjFace *face);
121 static const char *parse_map();
124 static map<string, Material> matlib;
127 #define INVALID_IDX INT_MIN
129 #define SEP " \t\n\r\v"
131 bool Scene::load_obj(FILE *fp)
139 sprintf(cur_name, "default%02d.obj", seq++);
140 obj.cur_obj = cur_name;
142 int prev_cmd = 0, obj_added = 0;
148 fgets(line, sizeof line, fp);
154 if(!(tok = strtok(line, SEP))) {
155 continue; // ignore empty lines
159 if((cmd = get_cmd(tok)) == -1) {
160 continue; // ignore unknown commands ...
165 if(!parse_vec(&vec)) {
168 obj.v.push_back(vec);
172 if(!parse_vec(&vec)) {
175 obj.vn.push_back(vec);
179 if(!parse_vec(&vec)) {
183 obj.vt.push_back(Vector2(vec.x, vec.y));
188 if(prev_cmd == CMD_O || prev_cmd == CMD_G) {
189 break; // just in case we've got both of them in a row
191 /* if we have any previous data, group them up, add the object
192 * and continue with the new one...
195 Object *robj = cons_object(&obj);
196 robj->mtl = matlib[obj.cur_mat];
200 obj.f.clear(); // clean the face list
202 if((tok = strtok(0, SEP))) {
205 sprintf(cur_name, "default%02d.obj", seq++);
206 obj.cur_obj = cur_name;
211 if((tok = strtok(0, SEP))) {
213 if(!(mfile = fopen(tok, "rb"))) {
214 fprintf(stderr, "failed to open material library: %s\n", tok);
218 // load all materials of the mtl file into a vector
220 if(!read_materials(mfile, &vmtl)) {
225 // and add them all to the scene
226 for(size_t i=0; i<vmtl.size(); i++) {
228 mat.ambient = vmtl[i].ambient;
229 mat.diffuse = vmtl[i].diffuse;
230 mat.specular = vmtl[i].specular;
231 mat.shininess = vmtl[i].shininess;
232 mat.emissive = vmtl[i].emissive;
233 mat.alpha = vmtl[i].alpha;
235 if(vmtl[i].tex_dif.length()) {
236 mat.tex[TEX_DIFFUSE] = load_texture(vmtl[i].tex_dif.c_str());
238 if(vmtl[i].tex_refl.length()) {
239 mat.tex[TEX_ENVMAP] = load_texture(vmtl[i].tex_refl.c_str());
242 matlib[vmtl[i].name] = mat;
248 if((tok = strtok(0, SEP))) {
256 if(!parse_face(&face)) {
260 // convert negative indices to regular indices
261 for(int i=0; i<4; i++) {
262 if(face.v[i] < 0 && face.v[i] != INVALID_IDX) {
263 face.v[i] = obj.v.size() + face.v[i];
265 if(face.n[i] < 0 && face.n[i] != INVALID_IDX) {
266 face.n[i] = obj.vn.size() + face.n[i];
268 if(face.t[i] < 0 && face.t[i] != INVALID_IDX) {
269 face.t[i] = obj.vt.size() + face.t[i];
273 // break quads into triangles if needed
274 obj.f.push_back(face);
276 face.v[1] = face.v[2];
277 face.n[1] = face.n[2];
278 face.t[1] = face.t[2];
280 face.v[2] = face.v[3];
281 face.n[2] = face.n[3];
282 face.t[2] = face.t[3];
284 obj.f.push_back(face);
289 break; // ignore unknown commands
295 // reached end of file...
297 Object *robj = cons_object(&obj);
298 robj->mtl = matlib[obj.cur_mat];
303 return obj_added > 0;
306 static Object *cons_object(ObjFile *obj)
309 Vector3 *varr, *narr;
312 int nelem = obj->f.size() * 3;
314 assert(sizeof(Vector3) == 3 * sizeof(float));
315 assert(sizeof(Vector2) == 2 * sizeof(float));
319 varr = new Vector3[nelem];
320 narr = new Vector3[nelem];
321 tarr = new Vector2[nelem];
326 if(obj->cur_obj.length() > 0) {
327 robj->set_name(obj->cur_obj.c_str());
330 // need at least one of each element
331 bool added_norm = false, added_tc = false;
332 if(obj->vn.empty()) {
333 obj->vn.push_back(Vector3(0, 0, 0));
336 if(obj->vt.empty()) {
337 obj->vt.push_back(Vector2(0, 0));
341 for(size_t i=0; i<obj->f.size(); i++) {
342 for(int j=0; j<3; j++) {
344 ObjFace *f = &obj->f[i];
346 varr[idx] = obj->v[f->v[j]];
347 narr[idx] = obj->vn[f->n[j] < 0 ? 0 : f->n[j]];
349 float t = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].x;
350 float s = obj->vt[f->t[j] < 0 ? 0 : f->t[j]].y;
351 tarr[idx] = Vector2(t, s);
362 Mesh *mesh = new Mesh;
363 mesh->set_attrib(MESH_ATTR_VERTEX, nelem, 3, &varr->x);
364 mesh->set_attrib(MESH_ATTR_NORMAL, nelem, 3, &narr->x);
365 mesh->set_attrib(MESH_ATTR_TEXCOORD, nelem, 2, &tarr->x);
366 robj->set_mesh(mesh);
368 printf("loaded object %s: %d faces\n", obj->cur_obj.c_str(), nelem / 3);
376 static bool read_materials(FILE *fp, vector<ObjMat> *vmtl)
382 fgets(line, sizeof line, fp);
388 if(!(tok = strtok(line, SEP))) {
393 if((cmd = get_cmd(tok)) == -1) {
399 // add the previous material, and start a new one
400 if(mat.name.length() > 0) {
401 vmtl->push_back(mat);
404 if((tok = strtok(0, SEP))) {
410 parse_color(&mat.emissive);
414 parse_color(&mat.ambient);
418 parse_color(&mat.diffuse);
422 parse_color(&mat.specular);
426 if((tok = strtok(0, SEP)) && is_float(tok)) {
427 mat.shininess = atof(tok);
432 if((tok = strtok(0, SEP)) && is_float(tok)) {
441 if(parse_color(&c)) {
442 mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x;
448 mat.tex_dif = parse_map();
452 mat.tex_refl = parse_map();
460 if(mat.name.length() > 0) {
461 vmtl->push_back(mat);
466 static int get_cmd(char *str)
469 while((*s = toupper(*s))) s++;
471 for(int i=0; cmd_names[i]; i++) {
472 if(strcmp(str, cmd_names[i]) == 0) {
479 static bool is_int(const char *str)
482 strtol(str, &tmp, 10);
486 static bool is_float(const char *str)
493 static bool parse_vec(Vector3 *vec)
495 for(int i=0; i<3; i++) {
498 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
504 (*vec)[i] = atof(tok);
510 static bool parse_color(Color *col)
512 for(int i=0; i<3; i++) {
515 if(!(tok = strtok(0, SEP)) || !is_float(tok)) {
516 col->y = col->z = col->x;
517 return i > 0 ? true : false;
519 (*col)[i] = atof(tok);
524 static bool parse_face(ObjFace *face)
526 char *tok[] = {0, 0, 0, 0};
529 for(int i=0; i<4; i++) {
530 if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) {
531 if(i < 3) return false; // less than 3 verts? not a polygon
537 for(int i=0; i<4; i++) {
538 char *subtok = tok[i];
540 if(!subtok || !*subtok || !is_int(subtok)) {
544 face->v[i] = INVALID_IDX;
546 face->v[i] = atoi(subtok);
547 if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */
550 while(subtok && *subtok && *subtok != '/') {
553 if(subtok && *subtok && *++subtok && is_int(subtok)) {
554 face->t[i] = atoi(subtok);
555 if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */
557 face->t[i] = INVALID_IDX;
560 while(subtok && *subtok && *subtok != '/') {
563 if(subtok && *subtok && *++subtok && is_int(subtok)) {
564 face->n[i] = atoi(subtok);
565 if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */
567 face->n[i] = INVALID_IDX;
574 static const char *parse_map()
576 char *tok, *prev = 0;
578 while((tok = strtok(0, SEP))) {
582 return prev ? prev : "";