#ifndef lint static const char RCSid[] = "$Id: wfconv.c,v 2.14 2014/06/11 00:08:30 greg Exp $"; #endif /* * Load Wavefront .OBJ file and convert to triangles with mesh info. * Code borrowed largely from obj2rad.c */ #include "copyright.h" #include "standard.h" #include "cvmesh.h" #include "triangulate.h" #include typedef int VNDX[3]; /* vertex index (point,map,normal) */ #define CHUNKSIZ 1024 /* vertex allocation chunk size */ #define MAXARG 512 /* maximum # arguments in a statement */ static FVECT *vlist; /* our vertex list */ static int nvs; /* number of vertices in our list */ static FVECT *vnlist; /* vertex normal list */ static int nvns; static RREAL (*vtlist)[2]; /* map vertex list */ static int nvts; static char *inpfile; /* input file name */ static int havemats; /* materials available? */ static char material[256]; /* current material name */ static char group[256]; /* current group name */ static int lineno; /* current line number */ static int faceno; /* current face number */ static int getstmt(char *av[MAXARG], FILE *fp); static int cvtndx(VNDX vi, char *vs); static int putface(int ac, char **av); static OBJECT getmod(void); static int puttri(char *v1, char *v2, char *v3); static void freeverts(void); static int newv(double x, double y, double z); static int newvn(double x, double y, double z); static int newvt(double x, double y); static void syntax(char *er); void wfreadobj( /* read in .OBJ file and convert */ char *objfn ) { FILE *fp; char *argv[MAXARG]; int argc; int nstats, nunknown; if (objfn == NULL) { inpfile = ""; fp = stdin; } else if ((fp = fopen(inpfile=objfn, "r")) == NULL) { sprintf(errmsg, "cannot open \"%s\"", inpfile); error(USER, errmsg); } havemats = (nobjects > 0); nstats = nunknown = 0; material[0] = '\0'; group[0] = '\0'; lineno = 0; faceno = 0; /* scan until EOF */ while ( (argc = getstmt(argv, fp)) ) { switch (argv[0][0]) { case 'v': /* vertex */ switch (argv[0][1]) { case '\0': /* point */ if (badarg(argc-1,argv+1,"fff")) syntax("bad vertex"); newv(atof(argv[1]), atof(argv[2]), atof(argv[3])); break; case 'n': /* normal */ if (argv[0][2]) goto unknown; if (badarg(argc-1,argv+1,"fff")) syntax("bad normal"); if (!newvn(atof(argv[1]), atof(argv[2]), atof(argv[3]))) syntax("zero normal"); break; case 't': /* coordinate */ if (argv[0][2]) goto unknown; if (badarg(argc-1,argv+1,"ff")) goto unknown; newvt(atof(argv[1]), atof(argv[2])); break; default: goto unknown; } break; case 'f': /* face */ if (argv[0][1]) goto unknown; faceno++; switch (argc-1) { case 0: case 1: case 2: syntax("too few vertices"); break; case 3: if (!puttri(argv[1], argv[2], argv[3])) syntax("bad triangle"); break; default: if (!putface(argc-1, argv+1)) syntax("bad face"); break; } break; case 'u': /* usemtl/usemap */ if (!strcmp(argv[0], "usemap")) break; if (strcmp(argv[0], "usemtl")) goto unknown; if (argc > 1) strcpy(material, argv[1]); else material[0] = '\0'; break; case 'o': /* object name */ if (argv[0][1]) goto unknown; break; case 'g': /* group name */ if (argv[0][1]) goto unknown; if (argc > 1) strcpy(group, argv[1]); else group[0] = '\0'; break; case '#': /* comment */ break; default:; /* something we don't deal with */ unknown: nunknown++; break; } nstats++; } /* clean up */ freeverts(); fclose(fp); if (nunknown > 0) { sprintf(errmsg, "%d of %d statements unrecognized", nunknown, nstats); error(WARNING, errmsg); } } static int getstmt( /* read the next statement from fp */ char *av[MAXARG], FILE *fp ) { static char sbuf[MAXARG*16]; char *cp; int i; do { if (fgetline(cp=sbuf, sizeof(sbuf), fp) == NULL) return(0); i = 0; for ( ; ; ) { while (isspace(*cp) || *cp == '\\') { if (*cp == '\n') lineno++; *cp++ = '\0'; } if (!*cp) break; if (i >= MAXARG-1) { sprintf(errmsg, "%s: too many arguments near line %d (limit %d)\n", inpfile, lineno+1, MAXARG-1); break; } av[i++] = cp; while (*++cp && !isspace(*cp)) ; } av[i] = NULL; lineno++; } while (!i); return(i); } static int cvtndx( /* convert vertex string to index */ VNDX vi, char *vs ) { /* get point */ vi[0] = atoi(vs); if (vi[0] > 0) { if (vi[0]-- > nvs) return(0); } else if (vi[0] < 0) { vi[0] += nvs; if (vi[0] < 0) return(0); } else return(0); /* get map coord. */ while (*vs) if (*vs++ == '/') break; vi[1] = atoi(vs); if (vi[1] > 0) { if (vi[1]-- > nvts) return(0); } else if (vi[1] < 0) { vi[1] += nvts; if (vi[1] < 0) return(0); } else vi[1] = -1; /* get normal */ while (*vs) if (*vs++ == '/') break; vi[2] = atoi(vs); if (vi[2] > 0) { if (vi[2]-- > nvns) return(0); } else if (vi[2] < 0) { vi[2] += nvns; if (vi[2] < 0) return(0); } else vi[2] = -1; return(1); } /* determine dominant axis for triangle */ static int dominant_axis(char *v1, char *v2, char *v3) { VNDX v1i, v2i, v3i; FVECT e1, e2, vn; int i, imax; if (!cvtndx(v1i, v1) || !cvtndx(v2i, v2) || !cvtndx(v3i, v3)) return(-1); VSUB(e1, vlist[v2i[0]], vlist[v1i[0]]); VSUB(e2, vlist[v3i[0]], vlist[v2i[0]]); VCROSS(vn, e1, e2); for (i = imax = 2; i--; ) if (vn[i]*vn[i] > vn[imax]*vn[imax]) imax = i; return(vn[imax]*vn[imax] > FTINY*FTINY ? imax : -1); } /* callback for triangle output from polygon */ static int tri_out(const Vert2_list *tp, int a, int b, int c) { return( puttri( ((char **)tp->p)[a], ((char **)tp->p)[b], ((char **)tp->p)[c] ) ); } static int putface( /* put out an N-sided polygon */ int ac, char **av ) { Vert2_list *poly = polyAlloc(ac); int i, ax, ay; if (poly == NULL) return(0); poly->p = (void *)av; for (i = ac-3; i >= 0; i--) /* identify dominant axis */ if ((ax = dominant_axis(av[i], av[i+1], av[i+2])) >= 0) break; if (ax < 0) return(1); /* ignore degenerate face */ if (++ax >= 3) ax = 0; ay = ax; if (++ay >= 3) ay = 0; for (i = 0; i < ac; i++) { /* convert to 2-D polygon */ VNDX vi; if (!cvtndx(vi, av[i])) { error(WARNING, "bad vertex reference"); polyFree(poly); return(0); } poly->v[i].mX = vlist[vi[0]][ax]; poly->v[i].mY = vlist[vi[0]][ay]; } /* break into triangles & output */ if (!polyTriangulate(poly, &tri_out)) { sprintf(errmsg, "self-intersecting face with %d vertices", ac); error(WARNING, errmsg); } polyFree(poly); return(1); } static OBJECT getmod(void) /* get current modifier ID */ { char *mnam; OBJECT mod; if (!havemats) return(OVOID); if (!strcmp(material, VOIDID)) return(OVOID); if (material[0]) /* prefer usemtl statements */ mnam = material; else if (group[0]) /* else use group name */ mnam = group; else return(OVOID); mod = modifier(mnam); if (mod == OVOID) { sprintf(errmsg, "%s: undefined modifier \"%s\"", inpfile, mnam); error(USER, errmsg); } return(mod); } static int puttri( /* convert a triangle */ char *v1, char *v2, char *v3 ) { VNDX v1i, v2i, v3i; RREAL *v1c, *v2c, *v3c; RREAL *v1n, *v2n, *v3n; if (!cvtndx(v1i, v1) || !cvtndx(v2i, v2) || !cvtndx(v3i, v3)) { error(WARNING, "bad vertex reference"); return(0); } if (v1i[1]>=0 && v2i[1]>=0 && v3i[1]>=0) { v1c = vtlist[v1i[1]]; v2c = vtlist[v2i[1]]; v3c = vtlist[v3i[1]]; } else v1c = v2c = v3c = NULL; if (v1i[2]>=0 && v2i[2]>=0 && v3i[2]>=0) { v1n = vnlist[v1i[2]]; v2n = vnlist[v2i[2]]; v3n = vnlist[v3i[2]]; } else v1n = v2n = v3n = NULL; return(cvtri(getmod(), vlist[v1i[0]], vlist[v2i[0]], vlist[v3i[0]], v1n, v2n, v3n, v1c, v2c, v3c) >= 0); } static void freeverts(void) /* free all vertices */ { if (nvs) { free((void *)vlist); nvs = 0; } if (nvts) { free((void *)vtlist); nvts = 0; } if (nvns) { free((void *)vnlist); nvns = 0; } } static int newv( /* create a new vertex */ double x, double y, double z ) { if (!(nvs%CHUNKSIZ)) { /* allocate next block */ if (nvs == 0) vlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT)); else vlist = (FVECT *)realloc((void *)vlist, (nvs+CHUNKSIZ)*sizeof(FVECT)); if (vlist == NULL) error(SYSTEM, "out of memory in newv"); } /* assign new vertex */ vlist[nvs][0] = x; vlist[nvs][1] = y; vlist[nvs][2] = z; return(++nvs); } static int newvn( /* create a new vertex normal */ double x, double y, double z ) { if (!(nvns%CHUNKSIZ)) { /* allocate next block */ if (nvns == 0) vnlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT)); else vnlist = (FVECT *)realloc((void *)vnlist, (nvns+CHUNKSIZ)*sizeof(FVECT)); if (vnlist == NULL) error(SYSTEM, "out of memory in newvn"); } /* assign new normal */ vnlist[nvns][0] = x; vnlist[nvns][1] = y; vnlist[nvns][2] = z; if (normalize(vnlist[nvns]) == 0.0) return(0); return(++nvns); } static int newvt( /* create a new texture map vertex */ double x, double y ) { if (!(nvts%CHUNKSIZ)) { /* allocate next block */ if (nvts == 0) vtlist = (RREAL (*)[2])malloc(CHUNKSIZ*2*sizeof(RREAL)); else vtlist = (RREAL (*)[2])realloc((void *)vtlist, (nvts+CHUNKSIZ)*2*sizeof(RREAL)); if (vtlist == NULL) error(SYSTEM, "out of memory in newvt"); } /* assign new vertex */ vtlist[nvts][0] = x; vtlist[nvts][1] = y; return(++nvts); } static void syntax( /* report syntax error and exit */ char *er ) { sprintf(errmsg, "%s: Wavefront syntax error near line %d: %s\n", inpfile, lineno, er); error(USER, errmsg); }