ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/ot/wfconv.c
Revision: 2.18
Committed: Fri Apr 30 16:40:10 2021 UTC (3 years ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, HEAD
Changes since 2.17: +18 -5 lines
Log Message:
fix(obj2mesh): fixed normal reversal during some face triangulation

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.18 static const char RCSid[] = "$Id: wfconv.c,v 2.17 2021/03/11 17:00:58 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Load Wavefront .OBJ file and convert to triangles with mesh info.
6     * Code borrowed largely from obj2rad.c
7     */
8    
9     #include "copyright.h"
10     #include "standard.h"
11     #include "cvmesh.h"
12 greg 2.11 #include "triangulate.h"
13 greg 2.1 #include <ctype.h>
14    
15     typedef int VNDX[3]; /* vertex index (point,map,normal) */
16    
17     #define CHUNKSIZ 1024 /* vertex allocation chunk size */
18    
19 greg 2.9 #define MAXARG 512 /* maximum # arguments in a statement */
20 greg 2.1
21 greg 2.3 static FVECT *vlist; /* our vertex list */
22     static int nvs; /* number of vertices in our list */
23     static FVECT *vnlist; /* vertex normal list */
24     static int nvns;
25 schorsch 2.5 static RREAL (*vtlist)[2]; /* map vertex list */
26 greg 2.3 static int nvts;
27    
28     static char *inpfile; /* input file name */
29     static int havemats; /* materials available? */
30 greg 2.10 static char material[256]; /* current material name */
31     static char group[256]; /* current group name */
32 greg 2.3 static int lineno; /* current line number */
33     static int faceno; /* current face number */
34    
35 greg 2.13 static int getstmt(char *av[MAXARG], FILE *fp);
36     static int cvtndx(VNDX vi, char *vs);
37     static int putface(int ac, char **av);
38 schorsch 2.8 static OBJECT getmod(void);
39 greg 2.13 static int puttri(char *v1, char *v2, char *v3);
40 schorsch 2.8 static void freeverts(void);
41 greg 2.13 static int newv(double x, double y, double z);
42     static int newvn(double x, double y, double z);
43     static int newvt(double x, double y);
44 schorsch 2.8 static void syntax(char *er);
45 greg 2.1
46    
47 greg 2.3 void
48 schorsch 2.8 wfreadobj( /* read in .OBJ file and convert */
49     char *objfn
50     )
51 greg 2.1 {
52     FILE *fp;
53     char *argv[MAXARG];
54     int argc;
55     int nstats, nunknown;
56    
57     if (objfn == NULL) {
58     inpfile = "<stdin>";
59     fp = stdin;
60     } else if ((fp = fopen(inpfile=objfn, "r")) == NULL) {
61     sprintf(errmsg, "cannot open \"%s\"", inpfile);
62     error(USER, errmsg);
63     }
64 greg 2.3 havemats = (nobjects > 0);
65 greg 2.1 nstats = nunknown = 0;
66 greg 2.3 material[0] = '\0';
67     group[0] = '\0';
68 greg 2.1 lineno = 0; faceno = 0;
69     /* scan until EOF */
70 schorsch 2.6 while ( (argc = getstmt(argv, fp)) ) {
71 greg 2.1 switch (argv[0][0]) {
72     case 'v': /* vertex */
73     switch (argv[0][1]) {
74     case '\0': /* point */
75     if (badarg(argc-1,argv+1,"fff"))
76 greg 2.2 syntax("bad vertex");
77 greg 2.1 newv(atof(argv[1]), atof(argv[2]),
78     atof(argv[3]));
79     break;
80     case 'n': /* normal */
81     if (argv[0][2])
82     goto unknown;
83     if (badarg(argc-1,argv+1,"fff"))
84 greg 2.2 syntax("bad normal");
85 greg 2.1 if (!newvn(atof(argv[1]), atof(argv[2]),
86     atof(argv[3])))
87 greg 2.2 syntax("zero normal");
88 greg 2.1 break;
89 greg 2.2 case 't': /* coordinate */
90 greg 2.1 if (argv[0][2])
91     goto unknown;
92     if (badarg(argc-1,argv+1,"ff"))
93     goto unknown;
94     newvt(atof(argv[1]), atof(argv[2]));
95     break;
96     default:
97     goto unknown;
98     }
99     break;
100     case 'f': /* face */
101     if (argv[0][1])
102     goto unknown;
103     faceno++;
104     switch (argc-1) {
105     case 0: case 1: case 2:
106 greg 2.2 syntax("too few vertices");
107 greg 2.1 break;
108     case 3:
109     if (!puttri(argv[1], argv[2], argv[3]))
110 greg 2.2 syntax("bad triangle");
111 greg 2.1 break;
112     default:
113     if (!putface(argc-1, argv+1))
114 greg 2.2 syntax("bad face");
115 greg 2.1 break;
116     }
117     break;
118 greg 2.3 case 'u': /* usemtl/usemap */
119     if (!strcmp(argv[0], "usemap"))
120     break;
121     if (strcmp(argv[0], "usemtl"))
122 greg 2.1 goto unknown;
123 greg 2.3 if (argc > 1)
124     strcpy(material, argv[1]);
125     else
126     material[0] = '\0';
127 greg 2.1 break;
128     case 'o': /* object name */
129     if (argv[0][1])
130     goto unknown;
131     break;
132 greg 2.3 case 'g': /* group name */
133 greg 2.1 if (argv[0][1])
134     goto unknown;
135 greg 2.3 if (argc > 1)
136     strcpy(group, argv[1]);
137     else
138     group[0] = '\0';
139 greg 2.1 break;
140     case '#': /* comment */
141     break;
142     default:; /* something we don't deal with */
143     unknown:
144     nunknown++;
145     break;
146     }
147     nstats++;
148     }
149     /* clean up */
150     freeverts();
151     fclose(fp);
152     if (nunknown > 0) {
153     sprintf(errmsg, "%d of %d statements unrecognized",
154     nunknown, nstats);
155     error(WARNING, errmsg);
156     }
157     }
158    
159    
160 greg 2.3 static int
161 schorsch 2.8 getstmt( /* read the next statement from fp */
162 greg 2.10 char *av[MAXARG],
163 schorsch 2.8 FILE *fp
164     )
165 greg 2.1 {
166 greg 2.3 static char sbuf[MAXARG*16];
167 greg 2.10 char *cp;
168     int i;
169 greg 2.1
170     do {
171     if (fgetline(cp=sbuf, sizeof(sbuf), fp) == NULL)
172     return(0);
173     i = 0;
174     for ( ; ; ) {
175     while (isspace(*cp) || *cp == '\\') {
176     if (*cp == '\n')
177     lineno++;
178     *cp++ = '\0';
179     }
180 greg 2.9 if (!*cp)
181 greg 2.1 break;
182 greg 2.9 if (i >= MAXARG-1) {
183     sprintf(errmsg,
184     "%s: too many arguments near line %d (limit %d)\n",
185     inpfile, lineno+1, MAXARG-1);
186     break;
187     }
188 greg 2.1 av[i++] = cp;
189     while (*++cp && !isspace(*cp))
190     ;
191     }
192     av[i] = NULL;
193     lineno++;
194     } while (!i);
195    
196     return(i);
197     }
198    
199    
200 greg 2.3 static int
201 schorsch 2.8 cvtndx( /* convert vertex string to index */
202 greg 2.10 VNDX vi,
203     char *vs
204 schorsch 2.8 )
205 greg 2.1 {
206     /* get point */
207     vi[0] = atoi(vs);
208     if (vi[0] > 0) {
209     if (vi[0]-- > nvs)
210     return(0);
211     } else if (vi[0] < 0) {
212     vi[0] += nvs;
213     if (vi[0] < 0)
214     return(0);
215     } else
216     return(0);
217     /* get map coord. */
218     while (*vs)
219     if (*vs++ == '/')
220     break;
221     vi[1] = atoi(vs);
222     if (vi[1] > 0) {
223     if (vi[1]-- > nvts)
224     return(0);
225     } else if (vi[1] < 0) {
226     vi[1] += nvts;
227     if (vi[1] < 0)
228     return(0);
229     } else
230     vi[1] = -1;
231     /* get normal */
232     while (*vs)
233     if (*vs++ == '/')
234     break;
235     vi[2] = atoi(vs);
236     if (vi[2] > 0) {
237     if (vi[2]-- > nvns)
238     return(0);
239     } else if (vi[2] < 0) {
240     vi[2] += nvns;
241     if (vi[2] < 0)
242     return(0);
243     } else
244     vi[2] = -1;
245     return(1);
246     }
247    
248 greg 2.11 /* determine dominant axis for triangle */
249     static int
250     dominant_axis(char *v1, char *v2, char *v3)
251     {
252 greg 2.12 VNDX v1i, v2i, v3i;
253 greg 2.11 FVECT e1, e2, vn;
254     int i, imax;
255    
256 greg 2.12 if (!cvtndx(v1i, v1) || !cvtndx(v2i, v2) || !cvtndx(v3i, v3))
257     return(-1);
258     VSUB(e1, vlist[v2i[0]], vlist[v1i[0]]);
259     VSUB(e2, vlist[v3i[0]], vlist[v2i[0]]);
260 greg 2.11 VCROSS(vn, e1, e2);
261     for (i = imax = 2; i--; )
262     if (vn[i]*vn[i] > vn[imax]*vn[imax])
263     imax = i;
264 greg 2.16 return(vn[imax]*vn[imax] > FTINY*FTINY*FTINY*FTINY ? imax : -1);
265 greg 2.11 }
266    
267 greg 2.18 /* struct needed for triangulation callback */
268     typedef struct {
269     char **avl;
270     int rev;
271     } WFpoly;
272    
273 greg 2.11 /* callback for triangle output from polygon */
274     static int
275     tri_out(const Vert2_list *tp, int a, int b, int c)
276     {
277 greg 2.18 WFpoly * wp = (WFpoly *)tp->p;
278    
279     if (wp->rev)
280     return( puttri(wp->avl[c], wp->avl[b], wp->avl[a]) );
281    
282     return( puttri(wp->avl[a], wp->avl[b], wp->avl[c]) );
283 greg 2.11 }
284 greg 2.1
285 greg 2.3 static int
286 schorsch 2.8 putface( /* put out an N-sided polygon */
287     int ac,
288 greg 2.10 char **av
289 schorsch 2.8 )
290 greg 2.1 {
291 greg 2.17 Vert2_list *poly;
292 greg 2.18 WFpoly myps;
293 greg 2.11 int i, ax, ay;
294 greg 2.1
295 greg 2.11 for (i = ac-3; i >= 0; i--) /* identify dominant axis */
296     if ((ax = dominant_axis(av[i], av[i+1], av[i+2])) >= 0)
297     break;
298     if (ax < 0)
299 greg 2.12 return(1); /* ignore degenerate face */
300 greg 2.17 poly = polyAlloc(ac);
301     if (poly == NULL)
302     return(0);
303 greg 2.18 myps.avl = av;
304     poly->p = &myps;
305 greg 2.11 if (++ax >= 3) ax = 0;
306     ay = ax;
307     if (++ay >= 3) ay = 0;
308     for (i = 0; i < ac; i++) { /* convert to 2-D polygon */
309     VNDX vi;
310     if (!cvtndx(vi, av[i])) {
311     error(WARNING, "bad vertex reference");
312     polyFree(poly);
313 greg 2.1 return(0);
314 greg 2.11 }
315     poly->v[i].mX = vlist[vi[0]][ax];
316     poly->v[i].mY = vlist[vi[0]][ay];
317 greg 2.1 }
318 greg 2.18 /* flag for order reversal */
319     myps.rev = (polyArea(poly) < .0);
320 greg 2.11 /* break into triangles & output */
321 greg 2.13 if (!polyTriangulate(poly, &tri_out)) {
322     sprintf(errmsg, "self-intersecting face with %d vertices", ac);
323     error(WARNING, errmsg);
324     }
325 greg 2.11 polyFree(poly);
326     return(1);
327 greg 2.1 }
328    
329    
330 greg 2.3 static OBJECT
331 schorsch 2.8 getmod(void) /* get current modifier ID */
332 greg 2.3 {
333     char *mnam;
334     OBJECT mod;
335    
336     if (!havemats)
337     return(OVOID);
338     if (!strcmp(material, VOIDID))
339     return(OVOID);
340     if (material[0]) /* prefer usemtl statements */
341     mnam = material;
342     else if (group[0]) /* else use group name */
343     mnam = group;
344     else
345     return(OVOID);
346     mod = modifier(mnam);
347     if (mod == OVOID) {
348     sprintf(errmsg, "%s: undefined modifier \"%s\"",
349     inpfile, mnam);
350     error(USER, errmsg);
351     }
352     return(mod);
353     }
354    
355    
356     static int
357 schorsch 2.8 puttri( /* convert a triangle */
358     char *v1,
359     char *v2,
360     char *v3
361     )
362 greg 2.1 {
363     VNDX v1i, v2i, v3i;
364 schorsch 2.5 RREAL *v1c, *v2c, *v3c;
365     RREAL *v1n, *v2n, *v3n;
366 greg 2.2
367 greg 2.7 if (!cvtndx(v1i, v1) || !cvtndx(v2i, v2) || !cvtndx(v3i, v3)) {
368     error(WARNING, "bad vertex reference");
369 greg 2.1 return(0);
370 greg 2.7 }
371 greg 2.1 if (v1i[1]>=0 && v2i[1]>=0 && v3i[1]>=0) {
372     v1c = vtlist[v1i[1]];
373     v2c = vtlist[v2i[1]];
374     v3c = vtlist[v3i[1]];
375     } else
376     v1c = v2c = v3c = NULL;
377    
378     if (v1i[2]>=0 && v2i[2]>=0 && v3i[2]>=0) {
379     v1n = vnlist[v1i[2]];
380     v2n = vnlist[v2i[2]];
381     v3n = vnlist[v3i[2]];
382     } else
383     v1n = v2n = v3n = NULL;
384    
385 greg 2.3 return(cvtri(getmod(), vlist[v1i[0]], vlist[v2i[0]], vlist[v3i[0]],
386 greg 2.1 v1n, v2n, v3n, v1c, v2c, v3c) >= 0);
387     }
388    
389    
390 greg 2.3 static void
391 schorsch 2.8 freeverts(void) /* free all vertices */
392 greg 2.1 {
393     if (nvs) {
394     free((void *)vlist);
395     nvs = 0;
396     }
397     if (nvts) {
398     free((void *)vtlist);
399     nvts = 0;
400     }
401     if (nvns) {
402     free((void *)vnlist);
403     nvns = 0;
404     }
405     }
406    
407    
408 greg 2.3 static int
409 schorsch 2.8 newv( /* create a new vertex */
410     double x,
411     double y,
412     double z
413     )
414 greg 2.1 {
415     if (!(nvs%CHUNKSIZ)) { /* allocate next block */
416     if (nvs == 0)
417     vlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT));
418     else
419 greg 2.4 vlist = (FVECT *)realloc((void *)vlist,
420 greg 2.1 (nvs+CHUNKSIZ)*sizeof(FVECT));
421 greg 2.2 if (vlist == NULL)
422     error(SYSTEM, "out of memory in newv");
423 greg 2.1 }
424     /* assign new vertex */
425     vlist[nvs][0] = x;
426     vlist[nvs][1] = y;
427     vlist[nvs][2] = z;
428     return(++nvs);
429     }
430    
431    
432 greg 2.3 static int
433 schorsch 2.8 newvn( /* create a new vertex normal */
434     double x,
435     double y,
436     double z
437     )
438 greg 2.1 {
439     if (!(nvns%CHUNKSIZ)) { /* allocate next block */
440     if (nvns == 0)
441     vnlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT));
442     else
443 greg 2.4 vnlist = (FVECT *)realloc((void *)vnlist,
444 greg 2.1 (nvns+CHUNKSIZ)*sizeof(FVECT));
445 greg 2.2 if (vnlist == NULL)
446     error(SYSTEM, "out of memory in newvn");
447 greg 2.1 }
448     /* assign new normal */
449     vnlist[nvns][0] = x;
450     vnlist[nvns][1] = y;
451     vnlist[nvns][2] = z;
452     if (normalize(vnlist[nvns]) == 0.0)
453     return(0);
454     return(++nvns);
455     }
456    
457    
458 greg 2.3 static int
459 schorsch 2.8 newvt( /* create a new texture map vertex */
460     double x,
461     double y
462     )
463 greg 2.1 {
464     if (!(nvts%CHUNKSIZ)) { /* allocate next block */
465     if (nvts == 0)
466 schorsch 2.5 vtlist = (RREAL (*)[2])malloc(CHUNKSIZ*2*sizeof(RREAL));
467 greg 2.1 else
468 schorsch 2.5 vtlist = (RREAL (*)[2])realloc((void *)vtlist,
469     (nvts+CHUNKSIZ)*2*sizeof(RREAL));
470 greg 2.2 if (vtlist == NULL)
471     error(SYSTEM, "out of memory in newvt");
472 greg 2.1 }
473     /* assign new vertex */
474     vtlist[nvts][0] = x;
475     vtlist[nvts][1] = y;
476     return(++nvts);
477     }
478    
479    
480 greg 2.3 static void
481 schorsch 2.8 syntax( /* report syntax error and exit */
482     char *er
483     )
484 greg 2.1 {
485 greg 2.15 sprintf(errmsg, "%s: Wavefront syntax error near line %d: %s",
486 greg 2.1 inpfile, lineno, er);
487 greg 2.2 error(USER, errmsg);
488 greg 2.1 }