coeng

annotate src/objfile.c @ 8:8cce82794f90

seems to work nicely
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 15 Feb 2015 05:14:20 +0200
parents
children
rev   line source
nuclear@8 1 /* version 2: does not crash when there are no texture coordinates or normals */
nuclear@8 2 #include <stdio.h>
nuclear@8 3 #include <stdlib.h>
nuclear@8 4 #include <string.h>
nuclear@8 5 #include <errno.h>
nuclear@8 6 #include <limits.h>
nuclear@8 7 #include <ctype.h>
nuclear@8 8 #include "objfile.h"
nuclear@8 9
nuclear@8 10 #define INVALID_IDX INT_MIN
nuclear@8 11
nuclear@8 12 struct objfile {
nuclear@8 13 float *verts, *normals, *texcoords;
nuclear@8 14 int num_faces;
nuclear@8 15 };
nuclear@8 16
nuclear@8 17 struct vec3 {
nuclear@8 18 float x, y, z;
nuclear@8 19 };
nuclear@8 20
nuclear@8 21 struct face {
nuclear@8 22 int vidx[4];
nuclear@8 23 int nidx[4];
nuclear@8 24 int tidx[4];
nuclear@8 25 };
nuclear@8 26
nuclear@8 27 static int objf_read(struct objfile *obj, FILE *fp, const char *fname);
nuclear@8 28 static int count_elem(FILE *fp, const char *elem);
nuclear@8 29 static char *strip(char *s);
nuclear@8 30 static int mkface(char *line, struct face *face);
nuclear@8 31 static int parse_face(char *s, int *vidx, int *nidx, int *tidx);
nuclear@8 32 static int is_int(const char *str);
nuclear@8 33
nuclear@8 34
nuclear@8 35 struct objfile *objf_load(const char *fname)
nuclear@8 36 {
nuclear@8 37 FILE *fp;
nuclear@8 38 struct objfile *obj;
nuclear@8 39
nuclear@8 40 if(!(fp = fopen(fname, "rb"))) {
nuclear@8 41 fprintf(stderr, "objf_load: failed to open file: %s: %s\n", fname, strerror(errno));
nuclear@8 42 return 0;
nuclear@8 43 }
nuclear@8 44
nuclear@8 45 if(!(obj = malloc(sizeof *obj))) {
nuclear@8 46 perror("failed to allocate objfile structure\n");
nuclear@8 47 fclose(fp);
nuclear@8 48 return 0;
nuclear@8 49 }
nuclear@8 50
nuclear@8 51 if(objf_read(obj, fp, fname) == -1) {
nuclear@8 52 free(obj);
nuclear@8 53 obj = 0;
nuclear@8 54 }
nuclear@8 55 fclose(fp);
nuclear@8 56 return obj;
nuclear@8 57 }
nuclear@8 58
nuclear@8 59 void objf_free(struct objfile *obj)
nuclear@8 60 {
nuclear@8 61 if(obj) {
nuclear@8 62 free(obj->verts);
nuclear@8 63 free(obj->normals);
nuclear@8 64 free(obj->texcoords);
nuclear@8 65 free(obj);
nuclear@8 66 }
nuclear@8 67 }
nuclear@8 68
nuclear@8 69 int objf_vertex_count(struct objfile *obj)
nuclear@8 70 {
nuclear@8 71 return obj->num_faces * 3;
nuclear@8 72 }
nuclear@8 73
nuclear@8 74 int objf_face_count(struct objfile *obj)
nuclear@8 75 {
nuclear@8 76 return obj->num_faces;
nuclear@8 77 }
nuclear@8 78
nuclear@8 79 float *objf_vertices(struct objfile *obj)
nuclear@8 80 {
nuclear@8 81 return obj->verts;
nuclear@8 82 }
nuclear@8 83
nuclear@8 84 float *objf_normals(struct objfile *obj)
nuclear@8 85 {
nuclear@8 86 return obj->normals;
nuclear@8 87 }
nuclear@8 88
nuclear@8 89 float *objf_texcoords(struct objfile *obj)
nuclear@8 90 {
nuclear@8 91 return obj->texcoords;
nuclear@8 92 }
nuclear@8 93
nuclear@8 94
nuclear@8 95 float *objf_vertex(struct objfile *obj, int idx)
nuclear@8 96 {
nuclear@8 97 return obj->verts + idx * 3;
nuclear@8 98 }
nuclear@8 99
nuclear@8 100 float *objf_normal(struct objfile *obj, int idx)
nuclear@8 101 {
nuclear@8 102 return obj->normals + idx * 3;
nuclear@8 103 }
nuclear@8 104
nuclear@8 105 float *objf_texcoord(struct objfile *obj, int idx)
nuclear@8 106 {
nuclear@8 107 return obj->texcoords + idx * 2;
nuclear@8 108 }
nuclear@8 109
nuclear@8 110 static int objf_read(struct objfile *obj, FILE *fp, const char *fname)
nuclear@8 111 {
nuclear@8 112 int i, j, res = -1;
nuclear@8 113 int vcount, ncount, tcount, fcount;
nuclear@8 114 int num_verts;
nuclear@8 115 struct vec3 *varr, *narr, *tarr;
nuclear@8 116 struct vec3 *vptr, *nptr, *tptr;
nuclear@8 117 struct vec3 dummy_vec = {0, 0, 0};
nuclear@8 118 struct face *faces, *fptr;
nuclear@8 119 float *vdest, *ndest, *tdest;
nuclear@8 120 char buf[512];
nuclear@8 121
nuclear@8 122 vcount = count_elem(fp, "v");
nuclear@8 123 rewind(fp);
nuclear@8 124 ncount = count_elem(fp, "vn");
nuclear@8 125 rewind(fp);
nuclear@8 126 tcount = count_elem(fp, "vt");
nuclear@8 127 rewind(fp);
nuclear@8 128 fcount = count_elem(fp, "f");
nuclear@8 129 rewind(fp);
nuclear@8 130
nuclear@8 131 obj->num_faces = fcount;
nuclear@8 132
nuclear@8 133 if(!vcount || !fcount) {
nuclear@8 134 fprintf(stderr, "invalid or corrupted obj file: %s\n", fname);
nuclear@8 135 return -1;
nuclear@8 136 }
nuclear@8 137
nuclear@8 138 printf("Reading obj file: %s (%d vertices, %d faces)\n", fname, vcount, fcount);
nuclear@8 139
nuclear@8 140 vptr = varr = malloc(vcount * sizeof *varr);
nuclear@8 141 nptr = narr = ncount ? malloc(ncount * sizeof *narr) : &dummy_vec;
nuclear@8 142 tptr = tarr = tcount ? malloc(tcount * sizeof *tarr) : &dummy_vec;
nuclear@8 143 fptr = faces = malloc(fcount * sizeof *faces);
nuclear@8 144
nuclear@8 145 if(!varr || !narr || !tarr || !faces) {
nuclear@8 146 perror("can't allocate enough memory for the geometry");
nuclear@8 147 goto cleanup;
nuclear@8 148 }
nuclear@8 149
nuclear@8 150 while(fgets(buf, sizeof buf, fp)) {
nuclear@8 151 char *line;
nuclear@8 152
nuclear@8 153 if(!(line = strip(buf)) || *line == '#') {
nuclear@8 154 continue; /* ignore empty lines and comments */
nuclear@8 155 }
nuclear@8 156
nuclear@8 157 if(sscanf(line, "v %f %f %f", &vptr->x, &vptr->y, &vptr->z) == 3) {
nuclear@8 158 vptr++;
nuclear@8 159 } else if(sscanf(line, "vn %f %f %f", &nptr->x, &nptr->y, &nptr->z) == 3) {
nuclear@8 160 nptr++;
nuclear@8 161 } else if(sscanf(line, "vt %f %f", &tptr->x, &tptr->y) == 2) {
nuclear@8 162 tptr++;
nuclear@8 163 } else if(mkface(line, fptr) == 0) {
nuclear@8 164 fptr++;
nuclear@8 165 }
nuclear@8 166 }
nuclear@8 167
nuclear@8 168 /* now go forth and create the straight-up 3-vert-per-face vertex arrays */
nuclear@8 169 num_verts = obj->num_faces * 3;
nuclear@8 170
nuclear@8 171 vdest = obj->verts = malloc(num_verts * 3 * sizeof *obj->verts);
nuclear@8 172 ndest = obj->normals = malloc(num_verts * 3 * sizeof *obj->normals);
nuclear@8 173 tdest = obj->texcoords = malloc(num_verts * 2 * sizeof *obj->texcoords);
nuclear@8 174
nuclear@8 175 if(!obj->verts || !obj->normals || !obj->texcoords) {
nuclear@8 176 free(obj->verts);
nuclear@8 177 free(obj->normals);
nuclear@8 178 goto cleanup;
nuclear@8 179 }
nuclear@8 180
nuclear@8 181 /* for all faces */
nuclear@8 182 for(i=0; i<fcount; i++) {
nuclear@8 183 /* for the 3 vertices of each face */
nuclear@8 184 for(j=0; j<3; j++) {
nuclear@8 185 *vdest++ = varr[faces[i].vidx[j]].x;
nuclear@8 186 *vdest++ = varr[faces[i].vidx[j]].y;
nuclear@8 187 *vdest++ = varr[faces[i].vidx[j]].z;
nuclear@8 188
nuclear@8 189 *ndest++ = narr[faces[i].nidx[j]].x;
nuclear@8 190 *ndest++ = narr[faces[i].nidx[j]].y;
nuclear@8 191 *ndest++ = narr[faces[i].nidx[j]].z;
nuclear@8 192
nuclear@8 193 *tdest++ = tarr[faces[i].tidx[j]].x;
nuclear@8 194 *tdest++ = tarr[faces[i].tidx[j]].y;
nuclear@8 195 }
nuclear@8 196 }
nuclear@8 197 res = 0; /* success */
nuclear@8 198
nuclear@8 199 cleanup:
nuclear@8 200 free(varr);
nuclear@8 201 if(narr != &dummy_vec) {
nuclear@8 202 free(narr);
nuclear@8 203 }
nuclear@8 204 if(tarr != &dummy_vec) {
nuclear@8 205 free(tarr);
nuclear@8 206 }
nuclear@8 207 free(faces);
nuclear@8 208 return res;
nuclear@8 209 }
nuclear@8 210
nuclear@8 211 static int count_elem(FILE *fp, const char *elem)
nuclear@8 212 {
nuclear@8 213 int count = 0;
nuclear@8 214 char buf[512];
nuclear@8 215
nuclear@8 216 while(fgets(buf, sizeof buf, fp)) {
nuclear@8 217 if(buf[0] == elem[0] && (!elem[1] || buf[1] == elem[1])) {
nuclear@8 218 count++;
nuclear@8 219 }
nuclear@8 220 }
nuclear@8 221 return count;
nuclear@8 222 }
nuclear@8 223
nuclear@8 224
nuclear@8 225 static char *strip(char *s)
nuclear@8 226 {
nuclear@8 227 while(*s && isspace(*s)) {
nuclear@8 228 s++;
nuclear@8 229 }
nuclear@8 230 return s;
nuclear@8 231 }
nuclear@8 232
nuclear@8 233 static int mkface(char *s, struct face *face)
nuclear@8 234 {
nuclear@8 235 int nverts;
nuclear@8 236
nuclear@8 237 if(s[0] != 'f') {
nuclear@8 238 return -1;
nuclear@8 239 }
nuclear@8 240
nuclear@8 241 if((nverts = parse_face(s + 2, face->vidx, face->nidx, face->tidx)) == -1) {
nuclear@8 242 return -1;
nuclear@8 243 }
nuclear@8 244 if(nverts > 3) {
nuclear@8 245 fprintf(stderr, "warning face with more than 3 vertices found\n");
nuclear@8 246 }
nuclear@8 247 return 0;
nuclear@8 248 }
nuclear@8 249
nuclear@8 250 #define SEP " \t\v\n\r"
nuclear@8 251 /* returns the number of vertices on this face, or -1 on error. */
nuclear@8 252 static int parse_face(char *s, int *vidx, int *nidx, int *tidx)
nuclear@8 253 {
nuclear@8 254 int i, num_verts = 0;
nuclear@8 255 char *tok[] = {0, 0, 0, 0};
nuclear@8 256 char *subtok;
nuclear@8 257
nuclear@8 258 for(i=0; i<4; i++) {
nuclear@8 259 if((!(tok[i] = strtok(i == 0 ? s : 0, SEP)) || !is_int(tok[i]))) {
nuclear@8 260 if(i < 3) return -1;
nuclear@8 261 } else {
nuclear@8 262 num_verts++;
nuclear@8 263 }
nuclear@8 264 }
nuclear@8 265
nuclear@8 266 for(i=0; i<4; i++) {
nuclear@8 267 subtok = tok[i];
nuclear@8 268 if(!subtok || !*subtok || !is_int(subtok)) {
nuclear@8 269 if(i < 3) {
nuclear@8 270 return -1;
nuclear@8 271 }
nuclear@8 272 vidx[i] = INVALID_IDX;
nuclear@8 273 } else {
nuclear@8 274 vidx[i] = atoi(subtok);
nuclear@8 275 if(vidx[i] > 0) vidx[i]--; /* convert to 0-based */
nuclear@8 276 }
nuclear@8 277
nuclear@8 278 while(subtok && *subtok && *subtok != '/') {
nuclear@8 279 subtok++;
nuclear@8 280 }
nuclear@8 281 if(subtok && *subtok && *++subtok && is_int(subtok)) {
nuclear@8 282 tidx[i] = atoi(subtok);
nuclear@8 283 if(tidx[i] > 0) tidx[i]--; /* convert to 0-based */
nuclear@8 284 } else {
nuclear@8 285 tidx[i] = 0;
nuclear@8 286 }
nuclear@8 287
nuclear@8 288 while(subtok && *subtok && *subtok != '/') {
nuclear@8 289 subtok++;
nuclear@8 290 }
nuclear@8 291 if(subtok && *subtok && *++subtok && is_int(subtok)) {
nuclear@8 292 nidx[i] = atoi(subtok);
nuclear@8 293 if(nidx[i] > 0) nidx[i]--; /* convert to 0-based */
nuclear@8 294 } else {
nuclear@8 295 nidx[i] = 0;
nuclear@8 296 }
nuclear@8 297 }
nuclear@8 298
nuclear@8 299 return num_verts;
nuclear@8 300 }
nuclear@8 301
nuclear@8 302
nuclear@8 303 static int is_int(const char *str)
nuclear@8 304 {
nuclear@8 305 return isdigit(str[0]) ||
nuclear@8 306 (str[0] == '-' && isdigit(str[1])) ||
nuclear@8 307 (str[0] == '+' && isdigit(str[1]));
nuclear@8 308 }