nuclear@0: /* version 2: does not crash when there are no texture coordinates or normals */ nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "objfile.h" nuclear@0: nuclear@0: #define INVALID_IDX INT_MIN nuclear@0: nuclear@0: struct objfile { nuclear@0: float *verts, *normals, *texcoords; nuclear@0: int num_faces; nuclear@0: }; nuclear@0: nuclear@0: struct vec3 { nuclear@0: float x, y, z; nuclear@0: }; nuclear@0: nuclear@0: struct face { nuclear@0: int vidx[4]; nuclear@0: int nidx[4]; nuclear@0: int tidx[4]; nuclear@0: }; nuclear@0: nuclear@0: static int objf_read(struct objfile *obj, FILE *fp, const char *fname); nuclear@0: static int count_elem(FILE *fp, const char *elem); nuclear@0: static char *strip(char *s); nuclear@0: static int mkface(char *line, struct face *face); nuclear@0: static int parse_face(char *s, int *vidx, int *nidx, int *tidx); nuclear@0: static int is_int(const char *str); nuclear@0: nuclear@0: nuclear@0: struct objfile *objf_load(const char *fname) nuclear@0: { nuclear@0: FILE *fp; nuclear@0: struct objfile *obj; nuclear@0: nuclear@0: if(!(fp = fopen(fname, "rb"))) { nuclear@0: fprintf(stderr, "objf_load: failed to open file: %s: %s\n", fname, strerror(errno)); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(!(obj = malloc(sizeof *obj))) { nuclear@0: perror("failed to allocate objfile structure\n"); nuclear@0: fclose(fp); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(objf_read(obj, fp, fname) == -1) { nuclear@0: free(obj); nuclear@0: obj = 0; nuclear@0: } nuclear@0: fclose(fp); nuclear@0: return obj; nuclear@0: } nuclear@0: nuclear@0: void objf_free(struct objfile *obj) nuclear@0: { nuclear@0: if(obj) { nuclear@0: free(obj->verts); nuclear@0: free(obj->normals); nuclear@0: free(obj->texcoords); nuclear@0: free(obj); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: int objf_vertex_count(struct objfile *obj) nuclear@0: { nuclear@0: return obj->num_faces * 3; nuclear@0: } nuclear@0: nuclear@0: int objf_face_count(struct objfile *obj) nuclear@0: { nuclear@0: return obj->num_faces; nuclear@0: } nuclear@0: nuclear@0: float *objf_vertices(struct objfile *obj) nuclear@0: { nuclear@0: return obj->verts; nuclear@0: } nuclear@0: nuclear@0: float *objf_normals(struct objfile *obj) nuclear@0: { nuclear@0: return obj->normals; nuclear@0: } nuclear@0: nuclear@0: float *objf_texcoords(struct objfile *obj) nuclear@0: { nuclear@0: return obj->texcoords; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: float *objf_vertex(struct objfile *obj, int idx) nuclear@0: { nuclear@0: return obj->verts + idx * 3; nuclear@0: } nuclear@0: nuclear@0: float *objf_normal(struct objfile *obj, int idx) nuclear@0: { nuclear@0: return obj->normals + idx * 3; nuclear@0: } nuclear@0: nuclear@0: float *objf_texcoord(struct objfile *obj, int idx) nuclear@0: { nuclear@0: return obj->texcoords + idx * 2; nuclear@0: } nuclear@0: nuclear@0: static int objf_read(struct objfile *obj, FILE *fp, const char *fname) nuclear@0: { nuclear@0: int i, j, res = -1; nuclear@0: int vcount, ncount, tcount, fcount; nuclear@0: int num_verts; nuclear@0: struct vec3 *varr, *narr, *tarr; nuclear@0: struct vec3 *vptr, *nptr, *tptr; nuclear@0: struct vec3 dummy_vec = {0, 0, 0}; nuclear@0: struct face *faces, *fptr; nuclear@0: float *vdest, *ndest, *tdest; nuclear@0: char buf[512]; nuclear@0: nuclear@0: vcount = count_elem(fp, "v"); nuclear@0: rewind(fp); nuclear@0: ncount = count_elem(fp, "vn"); nuclear@0: rewind(fp); nuclear@0: tcount = count_elem(fp, "vt"); nuclear@0: rewind(fp); nuclear@0: fcount = count_elem(fp, "f"); nuclear@0: rewind(fp); nuclear@0: nuclear@0: obj->num_faces = fcount; nuclear@0: nuclear@0: if(!vcount || !fcount) { nuclear@0: fprintf(stderr, "invalid or corrupted obj file: %s\n", fname); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: printf("Reading obj file: %s (%d vertices, %d faces)\n", fname, vcount, fcount); nuclear@0: nuclear@0: vptr = varr = malloc(vcount * sizeof *varr); nuclear@0: nptr = narr = ncount ? malloc(ncount * sizeof *narr) : &dummy_vec; nuclear@0: tptr = tarr = tcount ? malloc(tcount * sizeof *tarr) : &dummy_vec; nuclear@0: fptr = faces = malloc(fcount * sizeof *faces); nuclear@0: nuclear@0: if(!varr || !narr || !tarr || !faces) { nuclear@0: perror("can't allocate enough memory for the geometry"); nuclear@0: goto cleanup; nuclear@0: } nuclear@0: nuclear@0: while(fgets(buf, sizeof buf, fp)) { nuclear@0: char *line; nuclear@0: nuclear@0: if(!(line = strip(buf)) || *line == '#') { nuclear@0: continue; /* ignore empty lines and comments */ nuclear@0: } nuclear@0: nuclear@0: if(sscanf(line, "v %f %f %f", &vptr->x, &vptr->y, &vptr->z) == 3) { nuclear@0: vptr++; nuclear@0: } else if(sscanf(line, "vn %f %f %f", &nptr->x, &nptr->y, &nptr->z) == 3) { nuclear@0: nptr++; nuclear@0: } else if(sscanf(line, "vt %f %f", &tptr->x, &tptr->y) == 2) { nuclear@0: tptr++; nuclear@0: } else if(mkface(line, fptr) == 0) { nuclear@0: fptr++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: /* now go forth and create the straight-up 3-vert-per-face vertex arrays */ nuclear@0: num_verts = obj->num_faces * 3; nuclear@0: nuclear@0: vdest = obj->verts = malloc(num_verts * 3 * sizeof *obj->verts); nuclear@0: ndest = obj->normals = malloc(num_verts * 3 * sizeof *obj->normals); nuclear@0: tdest = obj->texcoords = malloc(num_verts * 2 * sizeof *obj->texcoords); nuclear@0: nuclear@0: if(!obj->verts || !obj->normals || !obj->texcoords) { nuclear@0: free(obj->verts); nuclear@0: free(obj->normals); nuclear@0: goto cleanup; nuclear@0: } nuclear@0: nuclear@0: /* for all faces */ nuclear@0: for(i=0; ividx, face->nidx, face->tidx)) == -1) { nuclear@0: return -1; nuclear@0: } nuclear@0: if(nverts > 3) { nuclear@0: fprintf(stderr, "warning face with more than 3 vertices found\n"); nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: #define SEP " \t\v\n\r" nuclear@0: /* returns the number of vertices on this face, or -1 on error. */ nuclear@0: static int parse_face(char *s, int *vidx, int *nidx, int *tidx) nuclear@0: { nuclear@0: int i, num_verts = 0; nuclear@0: char *tok[] = {0, 0, 0, 0}; nuclear@0: char *subtok; nuclear@0: nuclear@0: for(i=0; i<4; i++) { nuclear@0: if((!(tok[i] = strtok(i == 0 ? s : 0, SEP)) || !is_int(tok[i]))) { nuclear@0: if(i < 3) return -1; nuclear@0: } else { nuclear@0: num_verts++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: for(i=0; i<4; i++) { nuclear@0: subtok = tok[i]; nuclear@0: if(!subtok || !*subtok || !is_int(subtok)) { nuclear@0: if(i < 3) { nuclear@0: return -1; nuclear@0: } nuclear@0: vidx[i] = INVALID_IDX; nuclear@0: } else { nuclear@0: vidx[i] = atoi(subtok); nuclear@0: if(vidx[i] > 0) vidx[i]--; /* convert to 0-based */ nuclear@0: } nuclear@0: nuclear@0: while(subtok && *subtok && *subtok != '/') { nuclear@0: subtok++; nuclear@0: } nuclear@0: if(subtok && *subtok && *++subtok && is_int(subtok)) { nuclear@0: tidx[i] = atoi(subtok); nuclear@0: if(tidx[i] > 0) tidx[i]--; /* convert to 0-based */ nuclear@0: } else { nuclear@0: tidx[i] = 0; nuclear@0: } nuclear@0: nuclear@0: while(subtok && *subtok && *subtok != '/') { nuclear@0: subtok++; nuclear@0: } nuclear@0: if(subtok && *subtok && *++subtok && is_int(subtok)) { nuclear@0: nidx[i] = atoi(subtok); nuclear@0: if(nidx[i] > 0) nidx[i]--; /* convert to 0-based */ nuclear@0: } else { nuclear@0: nidx[i] = 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return num_verts; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: static int is_int(const char *str) nuclear@0: { nuclear@0: return isdigit(str[0]) || nuclear@0: (str[0] == '-' && isdigit(str[1])) || nuclear@0: (str[0] == '+' && isdigit(str[1])); nuclear@0: }