/* See {obj.h} */ /* Last edited on 2021-08-13 15:45:10 by stolfi */ /* From Y. Matsuzake's {cviewer.c} */ #include #include #include #include #include #include #include #include #include #include void obj_create(obj_format_t* obj) { obj->vertices = dyna_create(obj_vertex_t, 0); obj->textures = dyna_create(obj_texture_coord_t, 0); obj->normals = dyna_create(obj_normal_t, 0); obj->face_indices = dyna_create(int, 0); obj->face_descs = dyna_create(obj_face_desc_t, 0); } void obj_destroy(obj_format_t const* obj) { dyna_destroy(obj->vertices); dyna_destroy(obj->textures); dyna_destroy(obj->normals); dyna_destroy(obj->face_indices); dyna_destroy(obj->face_descs); } static int read_vertex(obj_vertex_t* v, char* line, uint32_t line_count) { int n = sscanf(line, "v %f %f %f %f", &v->x, &v->y, &v->z, &v->w); if(n == 3){ v->w = 1.f; }else if(n < 3){ log_err( "vertex with less than 3 elements at line %d: [%s]", line_count, line ); return 0; } return 1; } static int read_normal(obj_normal_t* n, char* line, uint32_t line_count) { int n_read = sscanf(line, "vn %f %f %f", &n->x, &n->y, &n->z); if(n_read != 3){ log_err( "expected 3 elements for normal, found %d at line %d: [%s]\n", n_read, line_count, line ); return 0; } return 1; } static int read_texture_coord( obj_texture_coord_t* t, char* line, uint32_t line_count) { int n_read = sscanf(line, "vt %f %f %f", &t->u, &t->v, &t->w); if(n_read < 3){ t->w = 0.f; if(n_read < 2){ t->v = 0.f; if(n_read < 1){ log_err( "expected at least one element for texture," " found %d at line %d: [%s]\n", n_read, line_count, line ); return 0; } } } return 1; } /* * there is 4 cases where reading the face could be: * 1. (V case) only vertices, only integers, ex: f 1 2 3 4 * 2. (VN case) vertices and normals, ex: f 1//4 4//5 1//4 1//5 * 4. (VT case) vertices and textures, ex: f 1/2 5/5 1/3 1/3 * 3. (VTN case) vertices, textures, and normals, ex: f 1/2/3 5/1/4 1/5/5 * * we could discover the case only counting the number of indices * and the number of bars */ static int read_face( int** face_indices, obj_face_desc_t* desc, char* line, uint32_t line_count) { desc->n_indices = 0; char* end; char* curr = next_digit(line); for(long n = strtol(curr, &end, 10); curr != NULL && *curr != '\0'; errno = 0, curr = next_digit(end), n = strtol(curr, &end, 10)){ ++(desc->n_indices); if(n > INT_MAX) log_war("possible overflow in line %d\n", line_count); if(n < INT_MIN) log_war("possible underflow in line %d\n", line_count); int ni = (int) n; *face_indices = dyna_add(*face_indices, &ni); if(errno != 0){ log_err("cout not read the face line %d\n", line_count); return 0; } } size_t n_bars = 0; curr = line; while((curr = strchr(curr, '/')) != NULL){ n_bars++; curr++; } // V case if(n_bars == 0){ desc->components = OBJ_HAS_ONLY_VERTEX; // VN case }else if(n_bars == desc->n_indices){ desc->components = OBJ_HAS_NORMAL; // VT case }else if(n_bars == desc->n_indices/2){ desc->components = OBJ_HAS_TEXTURE; // VTN case }else if(n_bars/2 == desc->n_indices/3){ desc->components = OBJ_HAS_NORMAL | OBJ_HAS_TEXTURE; }else{ log_err( "could not read face object, do not know the case at line %d\n", line_count ); return 0; } return 1; } int obj_read(obj_format_t* obj, FILE* in) { size_t n_read; char* line = NULL; obj_create(obj); uint32_t line_count = 0; while(getline(&line, &n_read, in) != -1){ line_count++; if(line[0] == '#') continue; if(beginswith(line, "v ")){ obj_vertex_t v; if(!read_vertex(&v, line, line_count)) return 0; obj->vertices = dyna_add(obj->vertices, &v); }else if(beginswith(line, "vt ")){ obj_texture_coord_t t; if(!read_texture_coord(&t, line, line_count)) return 0; obj->textures = dyna_add(obj->textures, &t); }else if(beginswith(line, "vn ")){ obj_normal_t n; if(!read_normal(&n, line, line_count)) return 0; obj->normals = dyna_add(obj->normals, &n); }else if(beginswith(line, "f ")){ obj_face_desc_t d; if(!read_face(&obj->face_indices, &d, line, line_count)) return 0; obj->face_descs = dyna_add(obj->face_descs, &d); }else{ log_war( "unsupported command on line %d: [%s]\n", line_count, line ); } } free(line); return 1; }