#include #include #include #include #include #include "model.h" typedef struct mread MREAD; typedef struct thing THING; struct thing { int n; int a; void **v; } ; struct model { THING materials; THING locations; THING points; THING triangles; THING objects; } ; struct mread { MODEL *m; FILE *f; FILE *errf; char *errmsg; int errlen; void (*throw)(void); } ; static char *getstr(MREAD *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static char *getstr(MREAD *R, const char *fmt, ...) { MODEL *m; char *s; int l; int a; int q; int bq; int c; int any; va_list ap; static void savechar(int ch) { if (l >= a) s = realloc(s,a=l+8); s[l++] = ch; } static void fail(const char *how) { fprintf(R->errf,"%s ",how); vfprintf(R->errf,fmt,ap); fprintf(R->errf,"\n"); free(s); (*R->throw)(); } m = R->m; va_start(ap,fmt); s = 0; l = 0; a = 0; q = 0; bq = 0; any = 0; while (1) { c = getc(R->f); switch (c) { case '\0': fail("NUL in"); break; case EOF: fail("unexpected EOF in"); break; } if (!q && !bq && isspace(c)) { if (any) break; continue; } any = 1; if (bq) { savechar(c); bq = 0; } else if (c == '\\') { bq = 1; } else if (c == q) { q = 0; } else if ((c == '"') || (c == '\'')) { q = c; } else { savechar(c); } } savechar(0); va_end(ap); return(s); } static unsigned int getuint(MREAD *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static unsigned int getuint(MREAD *R, const char *fmt, ...) { unsigned int v; va_list ap; if (fscanf(R->f,"%u",&v) != 1) { fprintf(R->errf,"can't read "); va_start(ap,fmt); vfprintf(R->errf,fmt,ap); va_end(ap); fprintf(R->errf,"\n"); (*R->throw)(); } return(v); } static XYZ getxyz(MREAD *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static XYZ getxyz(MREAD *R, const char *fmt, ...) { XYZ v; va_list ap; if (fscanf(R->f,"%lg%lg%lg",&v.x,&v.y,&v.z) != 3) { fprintf(R->errf,"can't read "); va_start(ap,fmt); vfprintf(R->errf,fmt,ap); va_end(ap); fprintf(R->errf,"\n"); (*R->throw)(); } return(v); } static double getfloat(MREAD *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static double getfloat(MREAD *R, const char *fmt, ...) { double v; va_list ap; if (fscanf(R->f,"%lg",&v) != 1) { fprintf(R->errf,"can't read "); va_start(ap,fmt); vfprintf(R->errf,fmt,ap); va_end(ap); fprintf(R->errf,"\n"); (*R->throw)(); } return(v); } static RGB getrgb(MREAD *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static RGB getrgb(MREAD *R, const char *fmt, ...) { double r; double g; double b; va_list ap; if (fscanf(R->f,"%lg%lg%lg",&r,&g,&b) != 3) { fprintf(R->errf,"can't read "); va_start(ap,fmt); vfprintf(R->errf,fmt,ap); va_end(ap); fprintf(R->errf,"\n"); (*R->throw)(); } if ((r < 0) || (r > 1) || (g < 0) || (g > 1) || (b < 0) || (b > 1)) { fprintf(R->errf,"out-of-range value in "); va_start(ap,fmt); vfprintf(R->errf,fmt,ap); va_end(ap); fprintf(R->errf,"\n"); (*R->throw)(); } return((RGB){.r=r,.g=g,.b=b}); } static unsigned char getonechar(MREAD *, const char *, ...) __attribute__((__format__(__printf__,2,3))); static unsigned char getonechar(MREAD *R, const char *fmt, ...) { char c; va_list ap; if (fscanf(R->f," %c",&c) != 1) { fprintf(R->errf,"can't read "); va_start(ap,fmt); vfprintf(R->errf,fmt,ap); va_end(ap); fprintf(R->errf,"\n"); (*R->throw)(); } return(c); } static int model_error(void *rv, const char *b, int l) { MREAD *r; r = rv; r->errmsg = realloc(r->errmsg,r->errlen+l+1); bcopy(b,r->errmsg+r->errlen,l); r->errlen += l; r->errmsg[r->errlen] = '\0'; return(l); } #define FNSET(R,S,I) \ int n_##S##s(MODEL *m) { return(m->S##s.n); } \ R *get_##S(MODEL *m, int x) { if ((x < 0) || (x >= m->S##s.n)) \ abort(); return(m->S##s.v[x]); } \ int new_##S(MODEL *m) { if (m->S##s.n >= m->S##s.a) m->S##s.v = \ realloc(m->S##s.v,(m->S##s.a=m->S##s.n+I)*sizeof(*m->S##s.v)); \ init_##S(m->S##s.v[m->S##s.n]=malloc(sizeof(R))); \ return(m->S##s.n++); } \ int del_##S(MODEL *m, int x) { if (deletable_##S(m,x)) { free_##S \ (m->S##s.v[x]); free(m->S##s.v[x]); if (x != m->S##s.n-1) m->S##s.v[x] \ = m->S##s.v[m->S##s.n-1]; m->S##s.n --; update_##S(m,m->S##s.n,x); \ return(0); } else { return(-1); } } static void init_material(MATERIAL *m) { m->name = 0; m->selflum = (RGB){.r=0,.g=0,.b=0}; m->reflect = (RGB){.r=0,.g=0,.b=0}; m->spec_exp = 0; m->spec_mul = 0; } static int deletable_material(MODEL *m, int x) { int i; for (i=m->triangles.n-1;i>=0;i--) if (((TRIANGLE *)m->triangles.v[i])->material == x) return(0); return(1); } static void free_material(MATERIAL *m) { free(m->name); } static void update_material(MODEL *m, int oldx, int newx) { int i; for (i=m->triangles.n-1;i>=0;i--) if (((TRIANGLE *)m->triangles.v[i])->material == oldx) ((TRIANGLE *)m->triangles.v[i])->material = newx; } FNSET(MATERIAL,material,4) static void init_location(XYZ *l) { *l = (XYZ){.x=0,.y=0,.z=0}; } static int deletable_location(MODEL *m, int x) { int i; for (i=m->points.n-1;i>=0;i--) if (((POINT *)m->points.v[i])->loc == x) return(0); return(1); } #define free_location(l) /* nothing */ static void update_location(MODEL *m, int oldx, int newx) { int i; for (i=m->points.n-1;i>=0;i--) if (((POINT *)m->points.v[i])->loc == oldx) ((POINT *)m->points.v[i])->loc = newx; } FNSET(XYZ,location,32) static void init_point(POINT *p) { p->type = PT_UNSET; p->loc = -1; p->normal = (XYZ){.x=0,.y=0,.z=0}; } static int deletable_point(MODEL *m, int x) { int i; for (i=m->triangles.n-1;i>=0;i--) { if ( (((TRIANGLE *)m->triangles.v[i])->corner[0] == x) || (((TRIANGLE *)m->triangles.v[i])->corner[1] == x) || (((TRIANGLE *)m->triangles.v[i])->corner[2] == x) ) return(0); } return(1); } #define free_point(p) /* nothing */ static void update_point(MODEL *m, int oldx, int newx) { int i; for (i=m->triangles.n-1;i>=0;i--) { if (((TRIANGLE *)m->triangles.v[i])->corner[0] == oldx) ((TRIANGLE *)m->triangles.v[i])->corner[0] = newx; if (((TRIANGLE *)m->triangles.v[i])->corner[1] == oldx) ((TRIANGLE *)m->triangles.v[i])->corner[1] = newx; if (((TRIANGLE *)m->triangles.v[i])->corner[2] == oldx) ((TRIANGLE *)m->triangles.v[i])->corner[2] = newx; } } FNSET(POINT,point,32) static void init_triangle(TRIANGLE *t) { t->material = -1; t->corner[0] = -1; t->corner[1] = -1; t->corner[2] = -1; } static int deletable_triangle(MODEL *m, int x) { int i; OBJECT *o; int j; for (i=m->objects.n-1;i>=0;i--) { o = m->objects.v[i]; for (j=o->ntriangles-1;j>=0;j--) { if (o->triangles[j] == x) return(0); } } return(1); } #define free_triangle(p) /* nothing */ static void update_triangle(MODEL *m, int oldx, int newx) { int i; OBJECT *o; int j; for (i=m->objects.n-1;i>=0;i--) { o = m->objects.v[i]; for (j=o->ntriangles-1;j>=0;j--) { if (o->triangles[j] == oldx) o->triangles[j] = newx; } } } FNSET(TRIANGLE,triangle,32) static void init_object(OBJECT *o) { o->ntriangles = -1; o->triangles = 0; } #define deletable_object(m,x) 1 static void free_object(OBJECT *o) { free(o->triangles); } #define update_object(m,o,n) /* nothing */ FNSET(OBJECT,object,8) #undef FNSET static void read_materials(MREAD *r) { MODEL *M; int i; M = r->m; for (i=0;imaterials.n;i++) { MATERIAL *m; m = malloc(sizeof(MATERIAL)); init_material(m); M->materials.v[i] = m; m->name = getstr(r,"material #%d name",i); m->selflum = getrgb(r,"material #%d self-lumninant colour",i); m->reflect = getrgb(r,"material #%d reflective colour",i); m->spec_exp = getfloat(r,"material #%d specularity exponent",i); m->spec_mul = getfloat(r,"material #%d specularity multiplier",i); } } static void read_locations(MREAD *r) { MODEL *M; int i; M = r->m; for (i=0;ilocations.n;i++) { XYZ *l; l = malloc(sizeof(XYZ)); init_location(l); M->locations.v[i] = l; *l = getxyz(r,"location #%d",i); } } static void read_points(MREAD *r) { MODEL *M; int i; M = r->m; for (i=0;ipoints.n;i++) { POINT *p; unsigned char t; p = malloc(sizeof(POINT)); init_point(p); M->points.v[i] = p; p->loc = getuint(r,"point #%d location",i); t = getonechar(r,"point #%d type",i); switch (t) { case 's': p->type = PT_SURFACE; p->normal = getxyz(r,"point #%d surface normal",i); break; case 'c': p->type = PT_CORNER; break; default: fprintf(r->errf,"point #%d type bad: `%c'\n",i,t); (*r->throw)(); break; } } } static void read_triangles(MREAD *r) { MODEL *M; int i; M = r->m; for (i=0;itriangles.n;i++) { TRIANGLE *t; int j; t = malloc(sizeof(TRIANGLE)); init_triangle(t); M->triangles.v[i] = t; t->material = getuint(r,"triangle #%d material",i); for (j=0;j<3;j++) t->corner[j] = getuint(r,"triangle #%d corner[%d]",i,j); } } static void read_objects(MREAD *r) { MODEL *M; int i; M = r->m; for (i=0;iobjects.n;i++) { OBJECT *o; int j; o = malloc(sizeof(OBJECT)); init_object(o); M->objects.v[i] = o; o->ntriangles = getuint(r,"object #%d triangle count",i); o->triangles = malloc(o->ntriangles*sizeof(int)); for (j=0;jntriangles;j++) o->triangles[j] = getuint(r,"object #%d triangle[%d]",i,j); } } static void read_v1(MREAD *r) { MODEL *M; int i; M = r->m; #define FOO(x) do {\ M->x##s.n = getuint(r,#x " count"); M->x##s.a = M->x##s.n; \ M->x##s.v = malloc(M->x##s.a*sizeof(void *)); \ for (i=M->x##s.n-1;i>=0;i--) M->x##s.v[i] = 0; \ } while (0) FOO(material); FOO(location); FOO(point); FOO(triangle); #undef FOO read_materials(r); read_locations(r); read_points(r); read_triangles(r); M->objects.n = 1; M->objects.a = 1; M->objects.v = malloc(sizeof(void *)); { OBJECT *o; o = malloc(sizeof(OBJECT)); M->objects.v[0] = o; o->ntriangles = M->triangles.n; o->triangles = malloc(M->triangles.n*sizeof(int)); for (i=M->triangles.n-1;i>=0;i--) o->triangles[i] = i; } } static void read_v2(MREAD *r) { MODEL *M; int i; M = r->m; #define FOO(x) do {\ M->x##s.n = getuint(r,#x " count"); M->x##s.a = M->x##s.n; \ M->x##s.v = malloc(M->x##s.a*sizeof(void *)); \ for (i=M->x##s.n-1;i>=0;i--) M->x##s.v[i] = 0; \ } while (0) FOO(material); FOO(location); FOO(point); FOO(triangle); FOO(object); #undef FOO read_materials(r); read_locations(r); read_points(r); read_triangles(r); read_objects(r); } static void init_thing(THING *t) { t->n = 0; t->a = 0; t->v = 0; } MODEL *load_model(FILE *f, void (*err)(const char *)) { __label__ throwto; MREAD *r; MODEL *m; char *ver; static void throw(void) { goto throwto; } m = new_model(); r = malloc(sizeof(MREAD)); r->m = m; r->f = f; r->errf = fwopen(r,&model_error); r->errmsg = 0; r->errlen = 0; r->throw = &throw; model_error(r,0,0); ver = getstr(r,"format version string"); if (!strcmp(ver,"ModelV1")) { free(ver); read_v1(r); } else if (!strcmp(ver,"ModelV2")) { free(ver); read_v2(r); } else { free(ver); fprintf(r->errf,"unrecognized file format string\n"); throw(); } fclose(r->errf); free(r->errmsg); free(r); return(m); throwto:; fclose(r->errf); (*err)(r->errmsg); free_model(m); free(r->errmsg); free(r); return(0); } MODEL *new_model(void) { MODEL *m; m = malloc(sizeof(MODEL)); init_thing(&m->materials); init_thing(&m->locations); init_thing(&m->points); init_thing(&m->triangles); init_thing(&m->objects); return(m); } static void print_string(FILE *f, const char *s) { for (;*s;s++) { switch (*s) { case '\'': case '"': case '\\': case ' ': case '\t': putc('\\',f); break; } putc(*s,f); } } static void print_rgb(FILE *f, RGB rgb) { fprintf(f,"%g %g %g",rgb.r,rgb.g,rgb.b); } static void print_xyz(FILE *f, XYZ xyz) { fprintf(f,"%g %g %g",xyz.x,xyz.y,xyz.z); } void save_model(MODEL *M, FILE *f) { int i; print_string(f,"ModelV2"); fprintf(f,"\n%d %d %d %d %d\n",M->materials.n,M->locations.n,M->points.n,M->triangles.n,M->objects.n); for (i=0;imaterials.n;i++) { MATERIAL *m; m = M->materials.v[i]; print_string(f,m->name); fprintf(f," "); print_rgb(f,m->selflum); fprintf(f," "); print_rgb(f,m->reflect); fprintf(f," %g %g\n",m->spec_exp,m->spec_mul); } for (i=0;ilocations.n;i++) { print_xyz(f,*((XYZ *)M->locations.v[i])); fprintf(f,"\n"); } for (i=0;ipoints.n;i++) { POINT *p; p = M->points.v[i]; fprintf(f,"%d ",p->loc); switch (p->type) { case PT_SURFACE: fprintf(f,"s "); print_xyz(f,p->normal); break; case PT_CORNER: fprintf(f,"c"); break; default: abort(); break; } fprintf(f,"\n"); } for (i=0;itriangles.n;i++) { TRIANGLE *t; t = M->triangles.v[i]; fprintf(f,"%d %d %d %d\n",t->material,t->corner[0],t->corner[1],t->corner[2]); } for (i=0;iobjects.n;i++) { OBJECT *o; int j; o = M->objects.v[i]; fprintf(f,"%d",o->ntriangles); for (j=0;jntriangles;j++) fprintf(f," %d",o->triangles[j]); fprintf(f,"\n"); } } void free_model(MODEL *m) { int i; for (i=m->materials.n-1;i>=0;i--) { free(((MATERIAL *)m->materials.v[i])->name); free(m->materials.v[i]); } free(m->materials.v); for (i=m->locations.n-1;i>=0;i--) free(m->locations.v[i]); free(m->locations.v); for (i=m->points.n-1;i>=0;i--) free(m->points.v[i]); free(m->points.v); for (i=m->triangles.n-1;i>=0;i--) free(m->triangles.v[i]); free(m->triangles.v); for (i=m->objects.n-1;i>=0;i--) { free(((OBJECT *)m->objects.v[i])->triangles); free(m->objects.v[i]); } free(m->objects.v); free(m); }