/* Project Ovejota, by Alejandro Valenzuela Roca (lanjoe9 mexinetica com) http://mexinetica.com/~lanjoe9/proyectos/ovejota Project Ovejota is an attempt to produce a free (as in freedom) library capable of viewing .OBJ files using OpenGL drawing instructions in C, for use in free software projects. This file is part of Project Ovejota. Project Ovejota is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Project Ovejota is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License (LICENSE.TXT) along with this program. If not, please see . This program was made possible by documentation found in: http://people.scs.fsu.edu/~burkardt/txt/obj_format.txt */ #include "ovejota.h" #ifndef NINTENDO_DS #ifndef NINTENDO_WII // FIXME: FTBFS in Nintendo DS /* WARNING: the line MUST be NULL-terminated. it doesn't matter whether there's a \n or not but it _MUST_ be NULL-terminated or you'll be properly tar'd & fsck'd!! */ void init_obj(ovj3d_obj * obj){ ovj3d_mtl * nomtl; list_init(&obj->groups); list_init(&obj->vertices); list_init(&obj->faces); list_init(&obj->normals); list_init(&obj->materials); list_init(&obj->instructions); list_init(&obj->texvs); //obj->scale.x = obj->scale.y = obj->scale.z = 1; obj->mtllib = NULL; obj->resource_path = NULL; nomtl = ovj3d_new_mtl("nomtl"); //debug("init OBJ\n"); list_append(CHANGE_MTL, (void *) nomtl, &obj->materials); } int parse_obj_line(char * line, ovj3d_obj * obj){ char * line_local; int r; int r2; int junk_warning = 0; int junk_error = 0; int integer_part, fract_part; int current_arg; int curr_num; char * str_ptr_ini = NULL; void * data; int keep_parsing = 1; int sign = 1; float argval[3]; int iargval = 0; char lastchar = 0; int face_vn = -1; int face_tx = -1; int num_diags_glob = 0, num_diags = 0; ovj3d_face_data * face_data; ovj3d_face * face; list_node * node; parse_status state = PARSE_NEUTRAL; parse_status last_state = PARSE_NEUTRAL; parse_status state_detail = PARSE_NEUTRAL; if (strlen(line)){ //printf("after: %d chars\n", strlen(line)); if (line[strlen(line)-1] != '\n'){ line_local = new char[strlen(line)+2]; //printf("Asociados %d + 1 carácteres\n",strlen(line)); strncpy(line_local, line, strlen(line)); line_local[strlen(line)] = '\n'; line_local[strlen(line)+1] = '\0'; } else { line_local = new char[strlen(line)+1]; //printf("Asociados %d carácteres\n",strlen(line)+1); strncpy(line_local, line, strlen(line)); line_local[strlen(line)] = '\0';; } for (r = 0; r < strlen(line_local); r++){ if ((line_local[r] == '\r') || (line_local[r] == '\t')){ line_local[r] = ' '; } } r = 0; //printf("parse: '%s'\n",line_local); do{ switch(state){ case PARSE_NEUTRAL:{ if ((line_local[r] == ' ') || (line_local[r] == '\n')){ state_detail = PARSE_WHITESPACE; } else if ((line_local[r] == 'u') || (line_local[r] == 'U')){ //usemtl str_ptr_ini = &line_local[r]; if (strncasecmp(str_ptr_ini, "usemtl", 6) == 0){ keep_parsing = 0; r2 = r + 6; do { r2++; //printf(" %c", line_local[r2]); } while ((r2 < strlen(line)) && (line_local[r2] == ' ')); str_ptr_ini = &line_local[r2]; //printf(" ! "); do { r2++; //printf(" %c", line_local[r2]); } while ((r2 < strlen(line)) && ((line_local[r2] != ' ') && (line_local[r2] != '\n') && (line_local[r2] != '#'))); line_local[r2] = '\0'; //debug("ToLink: %s\n", str_ptr_ini); data = (void * )new char[strlen(str_ptr_ini)+1]; strncpy((char *)data, str_ptr_ini, strlen(str_ptr_ini)); ((char * )data)[strlen(str_ptr_ini)] = '\0'; //printf("appending drawing instructions..\n"); list_append(CHANGE_MTL, data,&obj->instructions); line_local[r2] = ' '; } } else if ((line_local[r] == 'v') || (line_local[r] == 'V')){ /* Whitespace is expected next to the element */ //printf("v\n"); state_detail = PARSE_WHITESPACE; current_arg = 0; integer_part = 0; fract_part = 0; last_state = state; state = PARSE_VERTEX; if (r+1 < strlen(line_local)){ if ((line_local[r+1] == 't') || (line_local[r+1] =='T')){ state = PARSE_TEXV; r++; } else if ((line_local[r+1] == 'n') || (line_local[r+1] == 'N')){ state = PARSE_NORMAL; r++; } } } else if (line_local[r] == 'g'){ state = PARSE_NAME; state_detail = PARSE_WHITESPACE; } else if (line_local[r] == '#'){ last_state = state; state = PARSE_COMMENT; lastchar = line_local[r]; line_local[r] = '\0'; keep_parsing = 0; } else if ((line_local[r] == 'f') || (line_local[r] == 'F')){ state = PARSE_FACE; face_vn = 0; face = new ovj3d_face; list_init(&face->face_data); //debug("appending face..\n"); list_append(PARSE_FACE, (void *) face, &obj->faces); //debug("appending list..\n"); list_append(DRAW_FACELIST, (void *) face, &obj->instructions); //debug("New face created.. [%d]", obj->faces.count); state_detail = PARSE_WHITESPACE; } else if (line_local[r] == 'm'){ str_ptr_ini = &line_local[r]; if (strncmp("mtllib",str_ptr_ini, 6) ==0){ state = PARSE_MTLLIB; } } else { junk_warning = 1; } }break; case PARSE_FACE: { //debug("Parse face"); lastchar = line_local[r]; if ((line_local[r] == ' ') || (line_local[r] == '\n') || (line_local[r] == '/') || (line_local[r] == '#') || (line_local == '\0')) { if (state_detail == PARSE_INT){ state_detail = PARSE_WHITESPACE; line_local[r] = '\0';; sscanf(str_ptr_ini,"%d",&iargval); if (iargval > 0){ iargval--; } else { // Support for relative index references iargval += obj->vertices.count; } switch (num_diags){ case 0:{ // Vertex data face_data = new ovj3d_face_data; //printf(" %d\n", iargval); node = node_at(iargval,&obj->vertices); face_data->v = (vector3 *) node->data; face_data->vt = NULL; face_data->vn = NULL; //debug("Face data [v:%d]: %f, %f, %f\n", iargval, face_data->v->x, face_data->v->y, face_data->v->z); //debug("append facedata\n"); list_append(PARSE_FACE, (void *) face_data, &face->face_data); //printf(">_<*\n"); face_vn++; } break; case 1:{ node = node_at(iargval,&obj->texvs); face_data->vt = (vector2 *) node->data; //debug("appending texv..\n"); list_append(PARSE_TEXV, (void *) face_data, &face->face_data); } break; case 2:{ node = node_at(iargval,&obj->normals); face_data->vn = (vector3 *) node->data; //printf("Face data [v:%d]: %f, %f, %f\n", iargval, face_data->v->x, face_data->v->y, face_data->v->z); //debug("appending normal\n"); list_append(PARSE_NORMAL, (void *) face_data, &face->face_data); } break; } iargval = 0; line_local[r] = lastchar; } if (line_local[r] == '#'){ state_detail = PARSE_COMMENT; keep_parsing = 0; } } else if ((line_local[r] <= '9') && (line_local[r] >= '0')){ if (state_detail != PARSE_INT){ state_detail = PARSE_INT; str_ptr_ini = &line_local[r]; } //printf("argval\n"); //iargval *= 10; //iargval += line_local[r] - '0'; } else if (line_local[r] == '-'){ sign = -1; } if (lastchar == '/') { num_diags++; //debug("num_diags <- %d (++)",num_diags); } else if ((lastchar == ' ') || (lastchar == '\t') || (lastchar == '\0')|| (lastchar == '\n')) { num_diags = 0; //debug("num_diags <- %d (char: %c [%d])", num_diags, lastchar, lastchar); } }break; case PARSE_VERTEX: case PARSE_NORMAL:{ if ((line_local[r] == ' ') || (line_local[r] == '\t') || (line_local[r] == '\n')){ if (state_detail == PARSE_FLOAT){ if (current_arg < 3){ line_local[r] = '\0';// '\0'; sscanf(str_ptr_ini,"%f",&argval[current_arg]); current_arg++; } else { junk_warning = 1; } line_local[r] = ' '; state_detail = PARSE_WHITESPACE; } } else if ((line_local[r] =='.') || (line_local[r] == '-') || ((line_local[r] <= '9') && (line_local[r] >= '0'))){ if (state_detail != PARSE_FLOAT){ str_ptr_ini = &line_local[r]; state_detail = PARSE_FLOAT; } } }break; case PARSE_TEXV:{ if ((line_local[r] == ' ') || (line_local[r] == '\t') || (line_local[r] == '\n')){ if (state_detail == PARSE_FLOAT){ if (current_arg < 2){ line_local[r] = '\0'; sscanf(str_ptr_ini,"%f",&argval[current_arg]); current_arg++; } else { junk_warning = 1; } line_local[r] = ' '; state_detail = PARSE_WHITESPACE; } } else if ((line_local[r] =='.') || (line_local[r] == '-') || ((line_local[r] <= '9') && (line_local[r] >= '0'))){ if (state_detail != PARSE_FLOAT){ str_ptr_ini = &line_local[r]; state_detail = PARSE_FLOAT; } } }break; case PARSE_NAME:{ if (line_local[r] == '#'){ lastchar = line_local[r]; line_local[r] = '\0'; keep_parsing = 0; } else if ((line_local[r] == ' ') || (line_local[r] == '\t') || (line_local[r] == '\n')){ if (state_detail == PARSE_NAME){ lastchar = line_local[r]; line_local[r] = '\0'; keep_parsing = 0; } } else if (state_detail == PARSE_WHITESPACE) { state_detail = PARSE_NAME; str_ptr_ini = &line_local[r]; } }break; case PARSE_MTLLIB:{ //"mtllib" r2 = r + 5; //printf("Moving on: "); do { r2++; //printf(" %c", line_local[r2]); } while ((r2 < strlen(line)) && (line_local[r2] == ' ')); str_ptr_ini = &line_local[r2]; //printf(" ! "); do { r2++; //printf(" %c", line_local[r2]); } while ((r2 < strlen(line)) && ((line_local[r2] != ' ') && (line_local[r2] != '\n') && (line_local[r2] != '#'))); line_local[r2] = '\0'; keep_parsing = 0; obj->mtllib = new char[strlen(str_ptr_ini) + 1]; strncpy(obj->mtllib, str_ptr_ini, strlen(str_ptr_ini)); obj->mtllib[strlen(str_ptr_ini)] = '\0'; //debug("MTLLIB: '%s' [%d chr]",str_ptr_ini, strlen(str_ptr_ini)); line_local[r2] = ' '; } break; } r++; }while ((keep_parsing) && (r < strlen(line_local))); if ((r == strlen(line_local)) && ((state_detail == PARSE_FLOAT) || (state_detail == PARSE_INT))){ if (current_arg < 3){ sscanf(str_ptr_ini,"%f",&argval[current_arg]); current_arg++; } else { junk_warning = 1; } } if ((state == PARSE_VERTEX) || (state == PARSE_NORMAL) || (state == PARSE_TEXV)){ //printf("current_arg: %d\n",current_arg); //if (current_arg == 2){ //} else junk_error = 1; //printf("Got vertex data: %f, %f, %f\n",argval[0], argval[1], argval[2]); if ((state == PARSE_VERTEX) || (state == PARSE_NORMAL)){ data = (void * ) new vector3; v3_est(argval[0], argval[1], argval[2], (vector3 * ) data); if (state == PARSE_VERTEX){ if (obj->vertices.count == 0) { obj->bb[0].x = obj->bb[1].x = obj->bb[2].x = argval[0]; obj->bb[0].y = obj->bb[1].y = obj->bb[2].y = argval[1]; obj->bb[0].z = obj->bb[1].z = obj->bb[2].z = argval[2]; } else { if (argval[0] < obj->bb[0].x) { obj->bb[0].x = argval[0]; } else if (argval[0] > obj->bb[1].x) { obj->bb[1].x = argval[0]; } if (argval[1] < obj->bb[0].y) { obj->bb[0].y = argval[1]; } else if (argval[1] > obj->bb[1].y) { obj->bb[1].y = argval[1]; } if (argval[2] < obj->bb[0].z) { obj->bb[0].z = argval[2]; } else if (argval[2] > obj->bb[1].z) { obj->bb[1].z = argval[2]; } } //printf("append vertex\n"); list_append(PARSE_VERTEX, data, &obj->vertices); } else { //printf("append normal\n"); list_append(PARSE_NORMAL, data, &obj->normals); } } else{ data = (void * ) new vector2; ((vector2 *) data)->u = argval[0]; ((vector2 *) data)->v = argval[1]; //printf("append texvs\n"); list_append(PARSE_TEXV, data, &obj->texvs); } } if (state == PARSE_NAME){ if (state_detail != PARSE_WHITESPACE){ data = (void * ) new char [strlen(str_ptr_ini)+1]; strncpy((char * ) data,str_ptr_ini, strlen(str_ptr_ini)+1); list_append(PARSE_NAME, data, &obj->groups); //printf("New group:\"%s\"\n", (char *) data); line_local[r-1] = lastchar; } else { junk_warning = 1; } } //printf("Antes de del '%s'\n",line_local); delete line_local; //printf("Después de del\n"); if ((face_vn < 3) && (face_vn > 0)){ junk_error = 1; } //if (state == PARSE_FACE) //printf("\n"); } return ((junk_error*2)+junk_warning); } ovj3d_mtl * ovj3d_new_mtl(char * name){ ovj3d_mtl * res; res = new ovj3d_mtl; res->name = new char [strlen(name)+1]; res->name[strlen(name)] = '\0'; strncpy(res->name,name,strlen(name)); v3_est(MTL_ka_def, MTL_ka_def, MTL_ka_def, &res->ka); v3_est(MTL_kd_def, MTL_kd_def, MTL_kd_def, &res->kd); v3_est(MTL_ks_def, MTL_ks_def, MTL_ks_def, &res->ks); res->d = MTL_d_def; res->illum = MTL_illum_def; res->tr = MTL_tr_def; res->ns = MTL_ns_def; res->gl_texture = 0; res->map_ka = NULL; return res; } int parse_mtl_line(char * line, ovj3d_obj * obj, ovj3d_mtl ** current_mtl){ parse_status state = PARSE_NEUTRAL; parse_status state_detail = PARSE_NEUTRAL; char l, nxtchr; char * line_local; char * str_ptr_ini; vector3 * vector_ptr; int i, j; int keep_parsing; float argval[3]; int current_arg; char * mtlname; ovj3d_mtl * new_mtl = NULL; if (strlen(line)){ //printf("after: %d chars\n", strlen(line)); if (line[strlen(line)-1] != '\n'){ line_local = new char[strlen(line)+2]; //printf("Asociados %d + 1 carácteres\n",strlen(line)); strncpy(line_local, line, strlen(line)); line_local[strlen(line)] = '\n'; line_local[strlen(line)+1] = '\0'; } else { line_local = new char[strlen(line)+1]; //printf("Asociados %d carácteres\n",strlen(line)+1); strncpy(line_local, line, strlen(line)); line_local[strlen(line)] = '\0';; } for (i = 0; i < strlen(line_local); i++){ if ((line_local[i] == '\r') || (line_local[i] == '\t')){ line_local[i] = ' '; } } i = 0; keep_parsing = 1; current_arg = 0; do { l = line[i]; switch(l){ case 't': case 'T':{ if (state == PARSE_NEUTRAL){ if (i < strlen(line)){ nxtchr = line[i+1]; } else { nxtchr = '\0'; } if (nxtchr == 'r'){ state = PARSE_TR; state_detail = PARSE_WHITESPACE; } } } break; case 'd': case 'D':{ if (state == PARSE_NEUTRAL){ state = PARSE_D; state_detail = PARSE_WHITESPACE; } } break; case 'i': case 'I': case 'n': case 'm': case 'M': case 'N':{ if (state == PARSE_NEUTRAL){ if (i < strlen(line)){ nxtchr = line[i+1]; } else { nxtchr = '\0'; } if (((l == 'n') || (l == 'N' )) && ((nxtchr =='s') || (nxtchr == 'S'))){ state = PARSE_NS; state_detail = PARSE_WHITESPACE; } else { str_ptr_ini = &line_local[i]; //printf("set ptr_ini to %s[%d](%c)\n",str_ptr_ini,i,str_ptr_ini[0]); //if (l == 'm'){ // printf("m o_O\n"); //} state = PARSE_ELEMENT; state_detail = PARSE_ELEMENT; } } }break; case 'k': case 'K':{ if (state == PARSE_NEUTRAL){ if (i < strlen(line)){ nxtchr = line[i+1]; } else { nxtchr = '\0'; } //printf("K"); switch(nxtchr){ case 'a': case 'A':{ state = PARSE_KA; state_detail = PARSE_WHITESPACE; //printf("a\n"); } break; case 'd': case 'D':{ state = PARSE_KD; state_detail = PARSE_WHITESPACE; //printf("d\n"); } break; case 's': case 'S':{ state = PARSE_KS; state_detail = PARSE_WHITESPACE; //printf("s\n"); } break; } } } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.':{ //printf("num\n"); switch(state){ case PARSE_KA: case PARSE_KD: case PARSE_KS: case PARSE_NS: case PARSE_D: case PARSE_TR: case PARSE_ILLUM:{ //printf("Detecting float..\n"); if (state_detail == PARSE_WHITESPACE){ //printf("sw PARSE_WHITESPACE -> PARSE_FLOAT\n"); state_detail = PARSE_FLOAT; str_ptr_ini = &line_local[i]; } }break; } }break; case '#': //keep_parsing = 0; case ' ': case '\n':{ if (state_detail == PARSE_FLOAT){ line_local[i] = '\0'; sscanf(str_ptr_ini, "%f", &argval[current_arg]); //printf("Float parsed: %f\n",argval[current_arg]); if (current_arg == 0){ if (state == PARSE_NS){ (*current_mtl)->ns = argval[0]; //printf("Ns %f\n",(*current_mtl)->ns); } else if (state == PARSE_TR){ (*current_mtl)->tr = argval[0]; //printf("Tr %f\n",(*current_mtl)->tr); } else if (state == PARSE_D){ (*current_mtl)->d = argval[0]; //printf("d %f\n",(*current_mtl)->d); } else if (state == PARSE_ILLUM){ sscanf(str_ptr_ini, "%d", &((*current_mtl)->illum)); //printf("illum %d\n",(*current_mtl)->illum); } } else if (current_arg == 2){ if (state == PARSE_KA){ vector_ptr = &((*current_mtl)->ka); //printf("Ka_param\n"); } else if (state == PARSE_KD){ //printf("Kd_param\n"); vector_ptr = &((*current_mtl)->kd); } else if (state == PARSE_KS){ //printf("Ks_param\n"); vector_ptr = &((*current_mtl)->ks); } else { vector_ptr = NULL; } if (vector_ptr){ keep_parsing = 0; vector_ptr->x = argval[0]; vector_ptr->y = argval[1]; vector_ptr->z = argval[2]; //printf("Values: %f, %f, %f\n", vector_ptr->x, vector_ptr->y, vector_ptr->z); } } line_local[i] = l; state_detail = PARSE_WHITESPACE; current_arg++; } if (state == PARSE_ELEMENT){ line_local[i] = '\0'; //printf(" Parsing element.. %s\n",str_ptr_ini); if ((strcasecmp("map_kd",str_ptr_ini) == 0)||(strcasecmp("map_ka",str_ptr_ini) == 0)){ //printf("map_ka!\n"); state = PARSE_MAP_KA; j = i; //printf("%d]'%c'\n",j,l); do{ j++; } while((j < strlen(line)) && (line_local[j] == ' ')); if (strlen(line)>j){ str_ptr_ini = &line_local[j]; //printf("> PTR INI: %s\n",str_ptr_ini); do{ j++; } while(j < strlen(line) && (line_local[j] != ' ') && (line_local[j] != '\n'));// No más ".???->.bmp" line_local[j] = '\0'; //printf("storing map_ka: '%s'->",str_ptr_ini); (*current_mtl)->map_ka = new char[strlen(str_ptr_ini)+1]; strncpy((*current_mtl)->map_ka, str_ptr_ini, strlen(str_ptr_ini)); //printf("0x%x\n", (*current_mtl)->map_ka); (*current_mtl)->map_ka[strlen(str_ptr_ini)] = '\0'; //printf("'%s'\n",(*current_mtl)->map_ka); line_local[j] = ' '; //printf("Map_ka: '%s'\n",(*current_mtl)->map_ka); keep_parsing = 0; } state_detail = PARSE_WHITESPACE; }else if (strcasecmp("illum", str_ptr_ini) == 0){ state = PARSE_ILLUM; state_detail = PARSE_WHITESPACE; }else if (strcasecmp("newmtl",str_ptr_ini) == 0){ state = PARSE_NAME; state_detail = PARSE_WHITESPACE; line_local[i] = l; j = i; do{ j++; } while(j < strlen(line) && line_local[j] == ' '); if (strlen(line)-1>j){ str_ptr_ini = &line_local[j]; j++; do{ j++; } while(j < strlen(line) && (line_local[j] != '\n') && (line_local[j] != '#') && (line_local[j] != '\n') && (line_local[j] != ' ') && (line_local[j] != '\t')); line_local[j] = '\0'; // Allocate and initialize the new material new_mtl = ovj3d_new_mtl(str_ptr_ini); // Add the material to the materials list list_append(PARSE_MTLLIB, (void *) new_mtl, &obj->materials); // Set the current material *current_mtl = new_mtl; //printf("NewMTL added: '%s'\n",(*current_mtl)->name); } keep_parsing = 0; } line_local[i] = l; } }break; } i++; } while ((i < strlen(line)) && (keep_parsing)); if ((state == PARSE_NAME) && (state_detail == PARSE_WHITESPACE)){ //? } delete line_local; } return 0; } int link_obj_instructions(ovj3d_obj * obj){ int r; list_node * curr_node; ovj3d_mtl * material; curr_node = obj->instructions.first; //debug("Linking material names to pointers in drawing instructions...\n"); for (r = 0; r < obj->instructions.count-1; r++){ if (curr_node->type == CHANGE_MTL){ //printf("TNode: %d..\n", curr_node->type); //debug("CData: %s", (char *) curr_node->data); // Remove the material name and use the pointer to the material directly material = ovj3d_mtl_by_name((char * )curr_node->data, &obj->materials); //debug("Material: 0x%x, ",material); if (material){ delete ((char *) curr_node->data); curr_node->data = (void *) material; } else { debug("Warning: Unable to link %s to any material in material library, bogus mtl/obj?\n",(char * )curr_node->data); return 0; } } curr_node = curr_node->nxt; } //debug("End of link_obj_instructions"); return 1; } int load_obj_textures(ovj3d_obj * obj){ int r, path_clean, limit; char path[1024] = {'\0'}; list_node * curr_node; ovj3d_mtl * curr_mtl; curr_node = obj->materials.first; do{ curr_mtl = (ovj3d_mtl * ) curr_node->data; //debug("Material %s..\n",curr_mtl->name); if (curr_mtl->map_ka){ glEnable(GL_TEXTURE_2D); strncpy(path,obj->resource_path,strlen(obj->resource_path)); strncat(path,curr_mtl->map_ka,strlen(curr_mtl->map_ka)+1); //debug("Attempting to load '%s'\n", path); curr_mtl->gl_texture = mjGLTextureFromFile(path); } curr_node = curr_node->nxt; } while (curr_node); return 1; } int draw_obj(ovj3d_obj * obj, bool normals){ int r, curr_v; list_node * curr_node; list_node * vertex_node; ovj3d_mtl * material; ovj3d_face * face; ovj3d_face_data * face_data; // Save current settings glPushAttrib(GL_ALL_ATTRIB_BITS); // Set attributes needed for normal operation //glEnable(GL_COLOR_MATERIAL); mjEnableSimpleLighting(); mjEnableSimpleTexturing(ALPHA_ON); curr_node = obj->instructions.first; for (r = 0; r < obj->instructions.count-1; r++){ if (curr_node->type == CHANGE_MTL){ material = (ovj3d_mtl * )curr_node->data; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat * ) &material->ka); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat * ) &material->kd); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat * ) &material->ks); glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, (GLfloat * ) &material->ns); if (material->tr < 1.0) { glColor4f(1.0f, 1.0f, 1.0f, material->tr); } else { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } if (material->gl_texture){ glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, material->gl_texture); //printf("Enabling texture %d\n",material->gl_texture); } else { glDisable(GL_TEXTURE_2D); } } else { face = (ovj3d_face *) curr_node->data; glBegin(GL_POLYGON); //node = node_at(curr_f, &obj.faces); vertex_node = face->face_data.first; for (curr_v = 0; curr_v < face->face_data.count; curr_v++){ face_data = (ovj3d_face_data *) vertex_node->data; if ((face_data->vn) && normals) glNormal3f(face_data->vn->x, face_data->vn->y, face_data->vn->z); if (face_data->vt) glTexCoord2f(face_data->vt->u,face_data->vt->v); glVertex3f(face_data->v->x, face_data->v->y, face_data->v->z); vertex_node = vertex_node->nxt; } glEnd(); } curr_node = curr_node->nxt; } // Restore saved attributes glPopAttrib(); return 1; } ovj3d_mtl * ovj3d_mtl_by_name(char * name, list_header * materials){ int r, not_found= 1; list_node * curr_node; curr_node = materials->first; ovj3d_mtl * curr_mtl; do{ curr_mtl = (ovj3d_mtl * ) curr_node->data; not_found = strcasecmp(name, curr_mtl->name); //printf("C: %s/%s\n",name,curr_mtl->name); if (not_found) curr_node = curr_node->nxt; } while (curr_node && not_found); if (not_found) return NULL; return curr_mtl; } ovj3d_clone * parse_clone_line(char * line, ovj3d_clone * clone){ parse_status state = PARSE_NEUTRAL; parse_status state_detail = PARSE_NEUTRAL; ovj3d_clone * the_clone = NULL; char l, nxtchr; char * line_local; char * str_ptr_ini; vector3 * vector_ptr; int i, j; int keep_parsing = 1; float argval[9]; int current_arg; char * mtlname; if (strlen(line)){ //debug("after: %d chars\n", strlen(line)); if (line[strlen(line)-1] != '\n'){ line_local = new char[strlen(line)+2]; //debug("Asociados %d + 1 carácteres\n",strlen(line)); strncpy(line_local, line, strlen(line)); line_local[strlen(line)] = '\n'; line_local[strlen(line)+1] = '\0'; } else { line_local = new char[strlen(line)+1]; //debug("Asociados %d carácteres\n",strlen(line)+1); strncpy(line_local, line, strlen(line)); line_local[strlen(line)] = '\0';; } for (i = 0; i < strlen(line_local); i++){ if ((line_local[i] == '\r') || (line_local[i] == '\t') || (line_local[i] == '(') || (line_local[i] == ')')){ //debug("Replaced %c\n ",line_local[i]); line_local[i] = ' '; } } state = PARSE_WHITESPACE; state_detail = PARSE_WHITESPACE; i = 0; do { l = line_local[i]; switch(state){ case PARSE_WHITESPACE:{ if (l != ' '){ state = PARSE_ELEMENT; state_detail = PARSE_ELEMENT; str_ptr_ini = &line_local[i]; j = 0; } } break; case PARSE_ELEMENT:{ if (l == ','){ if (clone){ the_clone = clone; //debug("Modifying existing clone..\n"); } else { the_clone = new ovj3d_clone; init_clone(the_clone); //debug("New clone created..\n"); } //debug("Comparing '%s'", str_ptr_ini); if (strncasecmp("model", str_ptr_ini, 5) == 0){ //debug("Model\n"); state = PARSE_PATH; state_detail = PARSE_WHITESPACE; the_clone->type = OVJ3D_MODEL; } else if (strncasecmp("object",str_ptr_ini,6) == 0){ //debug("Object\n"); state = PARSE_NAME; state_detail = PARSE_WHITESPACE; the_clone->type = OVJ3D_OBJ; } else if (strncasecmp("item",str_ptr_ini, 4) == 0){ //debug("Item\n"); state = PARSE_NAME; state_detail = PARSE_WHITESPACE; the_clone->type = OVJ3D_ITEM; } else if (strncasecmp("character",str_ptr_ini, 9) == 0){ //debug("Character\n"); state = PARSE_NAME; state_detail = PARSE_WHITESPACE; the_clone->type = OVJ3D_CHARACTER; } } } break; case PARSE_PATH:{ if ((state_detail == PARSE_WHITESPACE) && (l == '"')){ state_detail = PARSE_PATH; str_ptr_ini = &line_local[i+1]; } else if ((state_detail == PARSE_PATH) && (l == '"')){ state = PARSE_NAME; state_detail = PARSE_WHITESPACE; line_local[i] = '\0'; the_clone->obj = new ovj3d_obj; init_obj(the_clone->obj); the_clone->obj->path = replace_dir_char(str_ptr_ini);//new char[strlen(str_ptr_ini)+1]; //strncpy(the_clone->obj->path,str_ptr_ini,strlen(str_ptr_ini)); //the_clone->obj->path[strlen(str_ptr_ini)] = '\0'; line_local[i] = l; /* Load the OBJ */ //debug("About to load object from file: '%s'\n",the_clone->obj->path); load_obj_from_file(the_clone->obj); //debug("Bounding box: (%f, %f, %f) - (%f, %f, %f)\n", //the_clone->obj->bb[0].x, the_clone->obj->bb[0].y, the_clone->obj->bb[0].z, //the_clone->obj->bb[1].x, the_clone->obj->bb[1].y, the_clone->obj->bb[1].z); /* OBJ loading complete.. */ } } break; case PARSE_NAME:{ if ((state_detail == PARSE_WHITESPACE) && (l == '"')){ state_detail = PARSE_NAME; str_ptr_ini = &line_local[i+1]; } else if ((state_detail == PARSE_NAME) && (l == '"')){ // if (the_clone->type == OVJ3D_MODEL){ keep_parsing = 0; } else { state = PARSE_MODELCONFIG; state_detail = PARSE_WHITESPACE; j = 0; } //state = PARSE_NAME; //state_detail = PARSE_WHITESPACE; line_local[i] = '\0'; the_clone->model_name = new char[strlen(str_ptr_ini)+1]; strncpy(the_clone->model_name,str_ptr_ini,strlen(str_ptr_ini)); the_clone->model_name[strlen(str_ptr_ini)] = '\0'; line_local[i] = l; } } break; case PARSE_MODELCONFIG:{ if (((l >= '0') && (l <= '9')) || (l == '.')|| (l == '-')){ if (state_detail == PARSE_WHITESPACE){ state_detail = PARSE_FLOAT; str_ptr_ini = &line_local[i]; } } else if (state_detail == PARSE_FLOAT){ state_detail = PARSE_WHITESPACE; line_local[i] = '\0'; sscanf(str_ptr_ini,"%f",&argval[j]); line_local[i] = l; if (j >= 8){ keep_parsing = 0; } j++; } } break; } i++; } while (keep_parsing && (i < strlen(line)) && ( l != '#')); if (j == 9){ // Establish modelconfig data v3_est(argval[0], argval[1], argval[2], &the_clone->pos); v3_est(argval[3], argval[4], argval[5], &the_clone->rot); v3_est(argval[6], argval[7], argval[8], &the_clone->scale); //printf("Setting data:\n" // "pos: %f, %f, %f\n" // "rot: %f, %f, %f\n" // "scl; %f, %f ,%f\n", // the_clone->pos.x, the_clone->pos.y, the_clone->pos.z, // the_clone->rot.x, the_clone->rot.y, the_clone->rot.z, // the_clone->scale.x, the_clone->scale.y, the_clone->scale.z); } } return the_clone; } void init_clone(ovj3d_clone * clone){ clone->obj = NULL; clone->model_name = NULL; clone->alias = NULL; clone->rot.x = clone->rot.y = clone->rot.z = clone->pos.x = clone->pos.y = clone->pos.z = 0.0; clone->scale.x = clone->scale.y = clone->scale.z = 1.0; //clone->type = NULL; } void load_clone_file(char * path, list_header * clone_list){ FILE * f; char buf[OVJ_MAX_LINE_LENGTH]; ovj3d_clone * clone; int i; char car_l; char * path_proc = replace_dir_char(path); if (f = fopen(path_proc,"r")){ i = 0; while (!feof(f) && i < OVJ_MAX_LINE_LENGTH){ fread(&car_l,1,1,f); buf[i] = car_l; if (buf[i] == '\n'){ buf[i+1] = '\0'; //printf("before: %d chars\n",strlen(buf)); //printf("parse: '%s'\n",buf); clone = parse_clone_line(buf,NULL); // Always create a new clone if (clone){ printf("Adding clone to list: %s\n", clone->model_name); list_append(clone->type, (void *) clone, clone_list); } //printf(":P\n"); i = -1; //buf[0] = NULL; } i++; } fclose(f); link_clones_to_models(clone_list); } else { //debug("Could not open clone file '%s'\n",path); } delete path_proc; } void link_clones_to_models(list_header * clones){ list_node * node; ovj3d_clone * clone, * found; node = clones->first; while(node){ if (node->type != OVJ3D_MODEL){ clone = (ovj3d_clone * ) node->data; if (clone->obj == NULL){ found = find_clone_by_name(clone->model_name, OVJ3D_MODEL, clones); clone->obj = found->obj; } } node = node->nxt; } } void load_obj_from_file(ovj3d_obj * obj){ FILE * f; char buf[OVJ_MAX_LINE_LENGTH]; int i; char car_l; char * mtllib_path; char * str_ptr_ini; ovj3d_mtl * current_mtl = NULL; if (f = fopen(obj->path,"r")){ i = 0; //printf("Parsing obj..\n"); while (!feof(f) && i < OVJ_MAX_LINE_LENGTH){ fread(&car_l,1,1,f); buf[i] = car_l; if (buf[i] == '\n'){ buf[i+1] = '\0'; //printf("before: %d chars\n",strlen(buf)); //printf("parseOBJ: '%s'\n",buf); parse_obj_line(buf, obj); //printf(":P\n"); i = -1; //buf[0] = NULL; } i++; } fclose(f); if (obj->mtllib){ //debug("Object has mtllib"); i = strlen(obj->path); //debug(":P"); while (i > -1){ i--; //debug("ObjPath[%d]: %c", i, obj->path[i]); if (obj->path[i] == SEPDIR){ mtllib_path = new char[i+strlen(obj->mtllib)+2]; obj->resource_path = new char[i+2]; strncpy(mtllib_path,obj->path,i+1); strncpy(obj->resource_path, obj->path,i+1); obj->resource_path[i+1] = '\0'; str_ptr_ini = &mtllib_path[i+1]; strncpy(str_ptr_ini,obj->mtllib,strlen(obj->mtllib)); mtllib_path[i+strlen(obj->mtllib)+1] = '\0'; debug("Resource path is '%s'", obj->resource_path); i = -1; } } if (!(obj->resource_path)) { mtllib_path = obj->mtllib; //debug ("mtllib_path set to %s",mtllib_path); } mtllib_path = replace_dir_char(mtllib_path); //printf("Opening mtllib file '%s'%s'\n", obj->resource_path, obj->mtllib); if (f = fopen(mtllib_path,"r")){ i = 0; while (!feof(f)){ fread(&car_l,1,1,f); buf[i] = car_l; if (buf[i] == '\n'){ buf[i+1] = '\0'; //printf("before: %d chars\n",strlen(buf)); //printf("parse: '%s'\n",buf); parse_mtl_line(buf, obj, ¤t_mtl); //printf(":P\n"); i = -1; //buf[0] = NULL; } i++; } fclose(f); //printf("%d materials, %d drawing instructions\n", obj.materials.count, obj.instructions.count); //printf("Loading textures...\n"); if (! link_obj_instructions(obj)){ debug("Error: Material linking failed\n"); } load_obj_textures(obj); } else { debug("Error when opening MTL file %s", mtllib_path); } delete mtllib_path; } calculateBB_BS(obj->bb, &obj->bsradius, &obj->bsradius_axis); debug("bb midpoint: (%f,%f,%f), radius: %f",obj->bb[2].x, obj->bb[2].y, obj->bb[2].z, obj->bsradius); } else { debug("Error when opening OBJ file %s\n", obj->path); } } ovj3d_clone * find_clone_by_name(char * name, ovejota_clones type, list_header * clones){ list_node * node; ovj3d_clone * clone; ovj3d_clone * result; int keep_searching = 1; node = clones->first; while(node && keep_searching){ if ((type == OVJ3D_ANY_CLONE) || (type == node->type)){ clone = (ovj3d_clone *) node->data; if (strncmp(name, clone->model_name, strlen(name)) == 0){ return clone; } } node = node->nxt; } return clone; } void calculateBB_BS(vector3 * bb, float * bsradius, char * bsradius_axis) { float L; float longestL = L = (bb[1].x - bb[0].x)/2.0; * bsradius_axis = 'x'; bb[2].x = bb[0].x + L; L = (bb[1].y - bb[0].y)/2.0; if (L > longestL) { longestL = L; * bsradius_axis = 'y'; } bb[2].y = bb[0].y + L; L = (bb[1].z - bb[0].z)/2.0; if (L > longestL){ longestL = L; * bsradius_axis = 'z'; } bb[2].z = bb[0].z + L; * bsradius = longestL; } #endif #endif