labyrinth

annotate src/objfile.c @ 0:8ba79034e8a6

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