/* This code was written by Micah Taylor, kixor.net, 2016 and is released into the Public Domain */ #ifndef OBJ_PARSER_H #define OBJ_PARSER_H #include // string_extra // extern "C" { static char strequal(const char *s1, const char *s2); static char contains(const char *haystack, const char *needle); static char strequal(const char *s1, const char *s2) { if(strcmp(s1, s2) == 0) return 1; return 0; } static char contains(const char *haystack, const char *needle) { if(strstr(haystack, needle) == NULL) return 0; return 1; } } // list // #define _CRT_SECURE_NO_WARNINGS #include #include #include typedef struct { int item_count; int current_max_size; char growable; void **items; char **names; } list; extern "C" { static void list_make(list *listo, int size, char growable); static int list_add_item(list *listo, void *item, char *name); static char* list_print_items(list *listo); static void* list_get_name(list *listo, char *name); static void* list_get_index(list *listo, int indx); static void* list_get_item(list *listo, void *item_to_find); static int list_find(list *listo, char *name_to_find); static void list_delete_index(list *listo, int indx); static void list_delete_name(list *listo, char *name); static void list_delete_item(list *listo, void *item); static void list_delete_all(list *listo); static void list_print_list(list *listo); static void list_free(list *listo); static void test_list(); // internal helper functions static char list_is_full(list *listo) { return(listo->item_count == listo->current_max_size); } static void list_grow(list *old_listo) { int i; list new_listo; list_make(&new_listo, old_listo->current_max_size*2, old_listo->growable++); for(i=0; icurrent_max_size; i++) list_add_item(&new_listo, old_listo->items[i] , old_listo->names[i]); list_free(old_listo); //copy new structure to old list old_listo->names = new_listo.names; old_listo->items = new_listo.items; old_listo->item_count = new_listo.item_count; old_listo->current_max_size = new_listo.current_max_size; old_listo->growable = new_listo.growable; } //end helpers static void list_make(list *listo, int start_size, char growable) { listo->names = (char**) malloc(sizeof(char*) * start_size); listo->items = (void**) malloc(sizeof(void*) * start_size); listo->item_count = 0; listo->current_max_size = start_size; listo->growable = growable; } static int list_add_item(list *listo, void *item, char *name) { int name_length; char *new_name; if( list_is_full(listo) ) { if( listo->growable ) list_grow(listo); else return -1; } listo->names[listo->item_count] = NULL; if(name != NULL) { name_length = strlen(name); new_name = (char*) malloc(sizeof(char) * name_length + 1); strncpy(new_name, name, name_length); listo->names[listo->item_count] = new_name; } listo->items[listo->item_count] = item; listo->item_count++; return listo->item_count-1; } static char* list_print_items(list *listo) { int i; for(i=0; i < listo->item_count; i++) { printf("%s\n", listo->names[i]); } return NULL; } static void* list_get_index(list *listo, int indx) { if(indx < listo->item_count) return listo->items[indx]; return NULL; } static void* list_get_item(list *listo, void *item_to_find) { int i = 0; for(i=0; i < listo->item_count; i++) { if(listo->items[i] == item_to_find) return listo->items[i]; } return NULL; } static void* list_get_name(list *listo, char *name_to_find) { int i = 0; for(i=0; i < listo->item_count; i++) { if(strncmp(listo->names[i], name_to_find, strlen(name_to_find)) == 0) return listo->items[i]; } return NULL; } static int list_find(list *listo, char *name_to_find) { int i = 0; for(i=0; i < listo->item_count; i++) { if(strncmp(listo->names[i], name_to_find, strlen(name_to_find)) == 0) return i; } return -1; } static void list_delete_item(list *listo, void *item) { int i; for(i=0; i < listo->item_count; i++) { if( listo->items[i] == item ) list_delete_index(listo, i); } } static void list_delete_name(list *listo, char *name) { int i; //int j; //char remove = 0; // int length_name = strlen(name); int item_name; if(name == NULL) return; for(i=0; i < listo->item_count; i++) { item_name = strlen(name); if( name != NULL && (strncmp(listo->names[i], name, strlen(name)) == 0) ) list_delete_index(listo, i); } } static void list_delete_index(list *listo, int indx) { int j; //remove item if(listo->names[indx] != NULL) free(listo->names[indx]); //restructure for(j=indx; j < listo->item_count-1; j++) { listo->names[j] = listo->names[j+1]; listo->items[j] = listo->items[j+1]; } listo->item_count--; return; } static void list_delete_all(list *listo) { int i; for(i=listo->item_count-1; i>=0; i--) list_delete_index(listo, i); } static void list_free(list *listo) { list_delete_all(listo); free(listo->names); free(listo->items); } static void list_print_list(list *listo) { int i; printf("count: %i/%i\n", listo->item_count, listo->current_max_size); for(i=0; i < listo->item_count; i++) { printf("list[%i]: %s\n", i, listo->names[i]); } } } // obj_parser // #define _CRT_SECURE_NO_WARNINGS #include #include #include #define OBJ_FILENAME_LENGTH 500 #define MATERIAL_NAME_SIZE 255 #define OBJ_LINE_SIZE 500 #define MAX_VERTEX_COUNT 4 //can only handle quads or triangles #define WHITESPACE " \t\n\r" typedef struct obj_face { int vertex_index[MAX_VERTEX_COUNT]; int normal_index[MAX_VERTEX_COUNT]; int texture_index[MAX_VERTEX_COUNT]; int vertex_count; int material_index; } obj_face; typedef struct obj_sphere { int pos_index; int up_normal_index; int equator_normal_index; int texture_index[MAX_VERTEX_COUNT]; int material_index; } obj_sphere; typedef struct obj_plane { int pos_index; int normal_index; int rotation_normal_index; int texture_index[MAX_VERTEX_COUNT]; int material_index; } obj_plane; typedef struct obj_vector { double e[3]; } obj_vector; typedef struct obj_material { char name[MATERIAL_NAME_SIZE]; char texture_filename[OBJ_FILENAME_LENGTH]; double amb[3]; double diff[3]; double spec[3]; double reflect; double refract; double trans; double shiny; double glossy; double refract_index; } obj_material; typedef struct obj_camera { int camera_pos_index; int camera_look_point_index; int camera_up_norm_index; } obj_camera; typedef struct obj_light_point { int pos_index; int material_index; } obj_light_point; typedef struct obj_light_disc { int pos_index; int normal_index; int material_index; } obj_light_disc; typedef struct obj_light_quad { int vertex_index[MAX_VERTEX_COUNT]; int material_index; } obj_light_quad; typedef struct obj_growable_scene_data { // vector extreme_dimensions[2]; char scene_filename[OBJ_FILENAME_LENGTH]; char material_filename[OBJ_FILENAME_LENGTH]; list vertex_list; list vertex_normal_list; list vertex_texture_list; list face_list; list sphere_list; list plane_list; list light_point_list; list light_quad_list; list light_disc_list; list material_list; obj_camera *camera; } obj_growable_scene_data; typedef struct obj_scene_data { obj_vector **vertex_list; obj_vector **vertex_normal_list; obj_vector **vertex_texture_list; obj_face **face_list; obj_sphere **sphere_list; obj_plane **plane_list; obj_light_point **light_point_list; obj_light_quad **light_quad_list; obj_light_disc **light_disc_list; obj_material **material_list; int vertex_count; int vertex_normal_count; int vertex_texture_count; int face_count; int sphere_count; int plane_count; int light_point_count; int light_quad_count; int light_disc_count; int material_count; obj_camera *camera; } obj_scene_data; extern "C" { static int parse_obj_scene(obj_scene_data *data_out, const char *filename); static void delete_obj_data(obj_scene_data *data_out); } static void obj_free_half_list(list *listo) { list_delete_all(listo); free(listo->names); } static int obj_convert_to_list_index(int current_max, int index) { if(index == 0) //no index return -1; if(index < 0) //relative to current list position return current_max + index; return index - 1; //normal counting index } static void obj_convert_to_list_index_v(int current_max, int *indices) { for(int i=0; iamb[0] = 0.2; mtl->amb[1] = 0.2; mtl->amb[2] = 0.2; mtl->diff[0] = 0.8; mtl->diff[1] = 0.8; mtl->diff[2] = 0.8; mtl->spec[0] = 1.0; mtl->spec[1] = 1.0; mtl->spec[2] = 1.0; mtl->reflect = 0.0; mtl->trans = 1; mtl->glossy = 98; mtl->shiny = 0; mtl->refract_index = 1; mtl->texture_filename[0] = '\0'; } static int obj_parse_vertex_index(int *vertex_index, int *texture_index, int *normal_index) { char *temp_str; char *token; int vertex_count = 0; while( (token = strtok(NULL, WHITESPACE)) != NULL) { if(texture_index != NULL) texture_index[vertex_count] = 0; if(normal_index != NULL) normal_index[vertex_count] = 0; vertex_index[vertex_count] = atoi( token ); if(contains(token, "//")) //normal only { temp_str = strchr(token, '/'); temp_str++; normal_index[vertex_count] = atoi( ++temp_str ); } else if(contains(token, "/")) { temp_str = strchr(token, '/'); texture_index[vertex_count] = atoi( ++temp_str ); if(contains(temp_str, "/")) { temp_str = strchr(temp_str, '/'); normal_index[vertex_count] = atoi( ++temp_str ); } } vertex_count++; } return vertex_count; } static obj_face* obj_parse_face(obj_growable_scene_data *scene) { int vertex_count; obj_face *face = (obj_face*)malloc(sizeof(obj_face)); vertex_count = obj_parse_vertex_index(face->vertex_index, face->texture_index, face->normal_index); obj_convert_to_list_index_v(scene->vertex_list.item_count, face->vertex_index); obj_convert_to_list_index_v(scene->vertex_texture_list.item_count, face->texture_index); obj_convert_to_list_index_v(scene->vertex_normal_list.item_count, face->normal_index); face->vertex_count = vertex_count; return face; } static obj_sphere* obj_parse_sphere(obj_growable_scene_data *scene) { int temp_indices[MAX_VERTEX_COUNT]; obj_sphere *obj = (obj_sphere*)malloc(sizeof(obj_sphere)); obj_parse_vertex_index(temp_indices, obj->texture_index, NULL); obj_convert_to_list_index_v(scene->vertex_texture_list.item_count, obj->texture_index); obj->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, temp_indices[0]); obj->up_normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[1]); obj->equator_normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[2]); return obj; } static obj_plane* obj_parse_plane(obj_growable_scene_data *scene) { int temp_indices[MAX_VERTEX_COUNT]; obj_plane *obj = (obj_plane*)malloc(sizeof(obj_plane)); obj_parse_vertex_index(temp_indices, obj->texture_index, NULL); obj_convert_to_list_index_v(scene->vertex_texture_list.item_count, obj->texture_index); obj->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, temp_indices[0]); obj->normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[1]); obj->rotation_normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[2]); return obj; } static obj_light_point* obj_parse_light_point(obj_growable_scene_data *scene) { obj_light_point *o= (obj_light_point*)malloc(sizeof(obj_light_point)); o->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, atoi( strtok(NULL, WHITESPACE)) ); return o; } static obj_light_quad* obj_parse_light_quad(obj_growable_scene_data *scene) { obj_light_quad *o = (obj_light_quad*)malloc(sizeof(obj_light_quad)); obj_parse_vertex_index(o->vertex_index, NULL, NULL); obj_convert_to_list_index_v(scene->vertex_list.item_count, o->vertex_index); return o; } static obj_light_disc* obj_parse_light_disc(obj_growable_scene_data *scene) { int temp_indices[MAX_VERTEX_COUNT]; obj_light_disc *obj = (obj_light_disc*)malloc(sizeof(obj_light_disc)); obj_parse_vertex_index(temp_indices, NULL, NULL); obj->pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, temp_indices[0]); obj->normal_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, temp_indices[1]); return obj; } static obj_vector* obj_parse_vector() { obj_vector *v = (obj_vector*)malloc(sizeof(obj_vector)); size_t const tokCount = 3; char const * tok; for(size_t i=0; ie[i] = atof( tok ); } return v; } static void obj_parse_camera(obj_growable_scene_data *scene, obj_camera *camera) { int indices[3]; obj_parse_vertex_index(indices, NULL, NULL); camera->camera_pos_index = obj_convert_to_list_index(scene->vertex_list.item_count, indices[0]); camera->camera_look_point_index = obj_convert_to_list_index(scene->vertex_list.item_count, indices[1]); camera->camera_up_norm_index = obj_convert_to_list_index(scene->vertex_normal_list.item_count, indices[2]); } static int obj_parse_mtl_file(const char *filename, list *material_list) { int line_number = 0; char *current_token; char current_line[OBJ_LINE_SIZE]; char material_open = 0; obj_material *current_mtl = NULL; FILE *mtl_file_stream; // open scene mtl_file_stream = fopen( filename, "r"); if(mtl_file_stream == 0) { fprintf(stderr, "Error reading file: %s\n", filename); return 0; } list_make(material_list, 10, 1); while( fgets(current_line, OBJ_LINE_SIZE, mtl_file_stream) ) { current_token = strtok( current_line, " \t\n\r"); line_number++; //skip comments if( current_token == NULL || strequal(current_token, "//") || strequal(current_token, "#")) continue; //start material else if( strequal(current_token, "newmtl")) { material_open = 1; current_mtl = (obj_material*) malloc(sizeof(obj_material)); obj_set_material_defaults(current_mtl); // get the name strncpy(current_mtl->name, strtok(NULL, " \t"), MATERIAL_NAME_SIZE); list_add_item(material_list, current_mtl, current_mtl->name); } //ambient else if( strequal(current_token, "Ka") && material_open) { current_mtl->amb[0] = atof( strtok(NULL, " \t")); current_mtl->amb[1] = atof( strtok(NULL, " \t")); current_mtl->amb[2] = atof( strtok(NULL, " \t")); } //diff else if( strequal(current_token, "Kd") && material_open) { current_mtl->diff[0] = atof( strtok(NULL, " \t")); current_mtl->diff[1] = atof( strtok(NULL, " \t")); current_mtl->diff[2] = atof( strtok(NULL, " \t")); } //specular else if( strequal(current_token, "Ks") && material_open) { current_mtl->spec[0] = atof( strtok(NULL, " \t")); current_mtl->spec[1] = atof( strtok(NULL, " \t")); current_mtl->spec[2] = atof( strtok(NULL, " \t")); } //shiny else if( strequal(current_token, "Ns") && material_open) { current_mtl->shiny = atof( strtok(NULL, " \t")); } //transparent else if( strequal(current_token, "d") && material_open) { current_mtl->trans = atof( strtok(NULL, " \t")); } //reflection else if( strequal(current_token, "r") && material_open) { current_mtl->reflect = atof( strtok(NULL, " \t")); } //glossy else if( strequal(current_token, "sharpness") && material_open) { current_mtl->glossy = atof( strtok(NULL, " \t")); } //refract index else if( strequal(current_token, "Ni") && material_open) { current_mtl->refract_index = atof( strtok(NULL, " \t")); } // illumination type else if( strequal(current_token, "illum") && material_open) { } // texture map else if( strequal(current_token, "map_Kd") && material_open) { strncpy(current_mtl->texture_filename, strtok(NULL, " \t"), OBJ_FILENAME_LENGTH); } else { fprintf(stderr, "Unknown command '%s' in material file %s at line %i:\n\t%s\n", current_token, filename, line_number, current_line); //return 0; } } fclose(mtl_file_stream); return 1; } static int obj_parse_obj_file(obj_growable_scene_data *growable_data, const char *filename) { FILE* obj_file_stream; int current_material = -1; char *current_token = NULL; char current_line[OBJ_LINE_SIZE]; int line_number = 0; // open scene obj_file_stream = fopen( filename, "r"); if(obj_file_stream == 0) { fprintf(stderr, "Error reading file: %s\n", filename); return 0; } char const * lastSlash = filename; while( NULL != strstr(lastSlash, "/")) lastSlash = strstr(lastSlash, "/")+1; size_t slashPos = lastSlash - filename; /* extreme_dimensions[0].x = INFINITY; extreme_dimensions[0].y = INFINITY; extreme_dimensions[0].z = INFINITY; extreme_dimensions[1].x = -INFINITY; extreme_dimensions[1].y = -INFINITY; extreme_dimensions[1].z = -INFINITY; if(v->x < extreme_dimensions[0].x) extreme_dimensions[0].x = v->x; if(v->x > extreme_dimensions[1].x) extreme_dimensions[1].x = v->x; if(v->y < extreme_dimensions[0].y) extreme_dimensions[0].y = v->y; if(v->y > extreme_dimensions[1].y) extreme_dimensions[1].y = v->y; if(v->z < extreme_dimensions[0].z) extreme_dimensions[0].z = v->z; if(v->z > extreme_dimensions[1].z) extreme_dimensions[1].z = v->z;*/ //parser loop while( fgets(current_line, OBJ_LINE_SIZE, obj_file_stream) ) { current_token = strtok( current_line, " \t\n\r"); line_number++; //skip comments if( current_token == NULL || current_token[0] == '#') continue; //parse objects else if( strequal(current_token, "v") ) //process vertex { list_add_item(&growable_data->vertex_list, obj_parse_vector(), NULL); } else if( strequal(current_token, "vn") ) //process vertex normal { list_add_item(&growable_data->vertex_normal_list, obj_parse_vector(), NULL); } else if( strequal(current_token, "vt") ) //process vertex texture { list_add_item(&growable_data->vertex_texture_list, obj_parse_vector(), NULL); } else if( strequal(current_token, "f") ) //process face { obj_face *face = obj_parse_face(growable_data); face->material_index = current_material; list_add_item(&growable_data->face_list, face, NULL); } else if( strequal(current_token, "sp") ) //process sphere { obj_sphere *sphr = obj_parse_sphere(growable_data); sphr->material_index = current_material; list_add_item(&growable_data->sphere_list, sphr, NULL); } else if( strequal(current_token, "pl") ) //process plane { obj_plane *pl = obj_parse_plane(growable_data); pl->material_index = current_material; list_add_item(&growable_data->plane_list, pl, NULL); } else if( strequal(current_token, "p") ) //process point { //make a small sphere to represent the point? } else if( strequal(current_token, "lp") ) //light point source { obj_light_point *o = obj_parse_light_point(growable_data); o->material_index = current_material; list_add_item(&growable_data->light_point_list, o, NULL); } else if( strequal(current_token, "ld") ) //process light disc { obj_light_disc *o = obj_parse_light_disc(growable_data); o->material_index = current_material; list_add_item(&growable_data->light_disc_list, o, NULL); } else if( strequal(current_token, "lq") ) //process light quad { obj_light_quad *o = obj_parse_light_quad(growable_data); o->material_index = current_material; list_add_item(&growable_data->light_quad_list, o, NULL); } else if( strequal(current_token, "c") ) //camera { growable_data->camera = (obj_camera*) malloc(sizeof(obj_camera)); obj_parse_camera(growable_data, growable_data->camera); } else if( strequal(current_token, "usemtl") ) // usemtl { current_material = list_find(&growable_data->material_list, strtok(NULL, WHITESPACE)); } else if( strequal(current_token, "mtllib") ) // mtllib { strncpy(growable_data->material_filename, filename, slashPos); strncpy(growable_data->material_filename+slashPos, strtok(NULL, WHITESPACE), OBJ_FILENAME_LENGTH-slashPos); obj_parse_mtl_file(growable_data->material_filename, &growable_data->material_list); continue; } else if( strequal(current_token, "o") ) //object name { } else if( strequal(current_token, "s") ) //smoothing { } else if( strequal(current_token, "g") ) // group { } else { printf("Unknown command '%s' in scene code at line %i: \"%s\".\n", current_token, line_number, current_line); } } fclose(obj_file_stream); return 1; } static void obj_init_temp_storage(obj_growable_scene_data *growable_data) { list_make(&growable_data->vertex_list, 10, 1); list_make(&growable_data->vertex_normal_list, 10, 1); list_make(&growable_data->vertex_texture_list, 10, 1); list_make(&growable_data->face_list, 10, 1); list_make(&growable_data->sphere_list, 10, 1); list_make(&growable_data->plane_list, 10, 1); list_make(&growable_data->light_point_list, 10, 1); list_make(&growable_data->light_quad_list, 10, 1); list_make(&growable_data->light_disc_list, 10, 1); list_make(&growable_data->material_list, 10, 1); growable_data->camera = NULL; } static void obj_free_temp_storage(obj_growable_scene_data *growable_data) { obj_free_half_list(&growable_data->vertex_list); obj_free_half_list(&growable_data->vertex_normal_list); obj_free_half_list(&growable_data->vertex_texture_list); obj_free_half_list(&growable_data->face_list); obj_free_half_list(&growable_data->sphere_list); obj_free_half_list(&growable_data->plane_list); obj_free_half_list(&growable_data->light_point_list); obj_free_half_list(&growable_data->light_quad_list); obj_free_half_list(&growable_data->light_disc_list); obj_free_half_list(&growable_data->material_list); } static void delete_obj_data(obj_scene_data *data_out) { int i; for(i=0; ivertex_count; i++) free(data_out->vertex_list[i]); free(data_out->vertex_list); for(i=0; ivertex_normal_count; i++) free(data_out->vertex_normal_list[i]); free(data_out->vertex_normal_list); for(i=0; ivertex_texture_count; i++) free(data_out->vertex_texture_list[i]); free(data_out->vertex_texture_list); for(i=0; iface_count; i++) free(data_out->face_list[i]); free(data_out->face_list); for(i=0; isphere_count; i++) free(data_out->sphere_list[i]); free(data_out->sphere_list); for(i=0; iplane_count; i++) free(data_out->plane_list[i]); free(data_out->plane_list); for(i=0; ilight_point_count; i++) free(data_out->light_point_list[i]); free(data_out->light_point_list); for(i=0; ilight_disc_count; i++) free(data_out->light_disc_list[i]); free(data_out->light_disc_list); for(i=0; ilight_quad_count; i++) free(data_out->light_quad_list[i]); free(data_out->light_quad_list); for(i=0; imaterial_count; i++) free(data_out->material_list[i]); free(data_out->material_list); free(data_out->camera); } static void obj_copy_to_out_storage(obj_scene_data *data_out, obj_growable_scene_data *growable_data) { data_out->vertex_count = growable_data->vertex_list.item_count; data_out->vertex_normal_count = growable_data->vertex_normal_list.item_count; data_out->vertex_texture_count = growable_data->vertex_texture_list.item_count; data_out->face_count = growable_data->face_list.item_count; data_out->sphere_count = growable_data->sphere_list.item_count; data_out->plane_count = growable_data->plane_list.item_count; data_out->light_point_count = growable_data->light_point_list.item_count; data_out->light_disc_count = growable_data->light_disc_list.item_count; data_out->light_quad_count = growable_data->light_quad_list.item_count; data_out->material_count = growable_data->material_list.item_count; data_out->vertex_list = (obj_vector**)growable_data->vertex_list.items; data_out->vertex_normal_list = (obj_vector**)growable_data->vertex_normal_list.items; data_out->vertex_texture_list = (obj_vector**)growable_data->vertex_texture_list.items; data_out->face_list = (obj_face**)growable_data->face_list.items; data_out->sphere_list = (obj_sphere**)growable_data->sphere_list.items; data_out->plane_list = (obj_plane**)growable_data->plane_list.items; data_out->light_point_list = (obj_light_point**)growable_data->light_point_list.items; data_out->light_disc_list = (obj_light_disc**)growable_data->light_disc_list.items; data_out->light_quad_list = (obj_light_quad**)growable_data->light_quad_list.items; data_out->material_list = (obj_material**)growable_data->material_list.items; data_out->camera = growable_data->camera; } static int parse_obj_scene(obj_scene_data *data_out, const char *filename) { obj_growable_scene_data growable_data; obj_init_temp_storage(&growable_data); if( obj_parse_obj_file(&growable_data, filename) == 0) return 0; //print_vector(NORMAL, "Max bounds are: ", &growable_data->extreme_dimensions[1]); //print_vector(NORMAL, "Min bounds are: ", &growable_data->extreme_dimensions[0]); obj_copy_to_out_storage(data_out, &growable_data); obj_free_temp_storage(&growable_data); return 1; } #endif