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

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: wfconv.c,v 2.17 2021/03/11 17:00:58 greg Exp $";
3 #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 #include "triangulate.h"
13 #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 #define MAXARG 512 /* maximum # arguments in a statement */
20
21 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 static RREAL (*vtlist)[2]; /* map vertex list */
26 static int nvts;
27
28 static char *inpfile; /* input file name */
29 static int havemats; /* materials available? */
30 static char material[256]; /* current material name */
31 static char group[256]; /* current group name */
32 static int lineno; /* current line number */
33 static int faceno; /* current face number */
34
35 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 static OBJECT getmod(void);
39 static int puttri(char *v1, char *v2, char *v3);
40 static void freeverts(void);
41 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 static void syntax(char *er);
45
46
47 void
48 wfreadobj( /* read in .OBJ file and convert */
49 char *objfn
50 )
51 {
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 havemats = (nobjects > 0);
65 nstats = nunknown = 0;
66 material[0] = '\0';
67 group[0] = '\0';
68 lineno = 0; faceno = 0;
69 /* scan until EOF */
70 while ( (argc = getstmt(argv, fp)) ) {
71 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 syntax("bad vertex");
77 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 syntax("bad normal");
85 if (!newvn(atof(argv[1]), atof(argv[2]),
86 atof(argv[3])))
87 syntax("zero normal");
88 break;
89 case 't': /* coordinate */
90 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 syntax("too few vertices");
107 break;
108 case 3:
109 if (!puttri(argv[1], argv[2], argv[3]))
110 syntax("bad triangle");
111 break;
112 default:
113 if (!putface(argc-1, argv+1))
114 syntax("bad face");
115 break;
116 }
117 break;
118 case 'u': /* usemtl/usemap */
119 if (!strcmp(argv[0], "usemap"))
120 break;
121 if (strcmp(argv[0], "usemtl"))
122 goto unknown;
123 if (argc > 1)
124 strcpy(material, argv[1]);
125 else
126 material[0] = '\0';
127 break;
128 case 'o': /* object name */
129 if (argv[0][1])
130 goto unknown;
131 break;
132 case 'g': /* group name */
133 if (argv[0][1])
134 goto unknown;
135 if (argc > 1)
136 strcpy(group, argv[1]);
137 else
138 group[0] = '\0';
139 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 static int
161 getstmt( /* read the next statement from fp */
162 char *av[MAXARG],
163 FILE *fp
164 )
165 {
166 static char sbuf[MAXARG*16];
167 char *cp;
168 int i;
169
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 if (!*cp)
181 break;
182 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 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 static int
201 cvtndx( /* convert vertex string to index */
202 VNDX vi,
203 char *vs
204 )
205 {
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 /* determine dominant axis for triangle */
249 static int
250 dominant_axis(char *v1, char *v2, char *v3)
251 {
252 VNDX v1i, v2i, v3i;
253 FVECT e1, e2, vn;
254 int i, imax;
255
256 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 VCROSS(vn, e1, e2);
261 for (i = imax = 2; i--; )
262 if (vn[i]*vn[i] > vn[imax]*vn[imax])
263 imax = i;
264 return(vn[imax]*vn[imax] > FTINY*FTINY*FTINY*FTINY ? imax : -1);
265 }
266
267 /* struct needed for triangulation callback */
268 typedef struct {
269 char **avl;
270 int rev;
271 } WFpoly;
272
273 /* callback for triangle output from polygon */
274 static int
275 tri_out(const Vert2_list *tp, int a, int b, int c)
276 {
277 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 }
284
285 static int
286 putface( /* put out an N-sided polygon */
287 int ac,
288 char **av
289 )
290 {
291 Vert2_list *poly;
292 WFpoly myps;
293 int i, ax, ay;
294
295 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 return(1); /* ignore degenerate face */
300 poly = polyAlloc(ac);
301 if (poly == NULL)
302 return(0);
303 myps.avl = av;
304 poly->p = &myps;
305 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 return(0);
314 }
315 poly->v[i].mX = vlist[vi[0]][ax];
316 poly->v[i].mY = vlist[vi[0]][ay];
317 }
318 /* flag for order reversal */
319 myps.rev = (polyArea(poly) < .0);
320 /* break into triangles & output */
321 if (!polyTriangulate(poly, &tri_out)) {
322 sprintf(errmsg, "self-intersecting face with %d vertices", ac);
323 error(WARNING, errmsg);
324 }
325 polyFree(poly);
326 return(1);
327 }
328
329
330 static OBJECT
331 getmod(void) /* get current modifier ID */
332 {
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 puttri( /* convert a triangle */
358 char *v1,
359 char *v2,
360 char *v3
361 )
362 {
363 VNDX v1i, v2i, v3i;
364 RREAL *v1c, *v2c, *v3c;
365 RREAL *v1n, *v2n, *v3n;
366
367 if (!cvtndx(v1i, v1) || !cvtndx(v2i, v2) || !cvtndx(v3i, v3)) {
368 error(WARNING, "bad vertex reference");
369 return(0);
370 }
371 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 return(cvtri(getmod(), vlist[v1i[0]], vlist[v2i[0]], vlist[v3i[0]],
386 v1n, v2n, v3n, v1c, v2c, v3c) >= 0);
387 }
388
389
390 static void
391 freeverts(void) /* free all vertices */
392 {
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 static int
409 newv( /* create a new vertex */
410 double x,
411 double y,
412 double z
413 )
414 {
415 if (!(nvs%CHUNKSIZ)) { /* allocate next block */
416 if (nvs == 0)
417 vlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT));
418 else
419 vlist = (FVECT *)realloc((void *)vlist,
420 (nvs+CHUNKSIZ)*sizeof(FVECT));
421 if (vlist == NULL)
422 error(SYSTEM, "out of memory in newv");
423 }
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 static int
433 newvn( /* create a new vertex normal */
434 double x,
435 double y,
436 double z
437 )
438 {
439 if (!(nvns%CHUNKSIZ)) { /* allocate next block */
440 if (nvns == 0)
441 vnlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT));
442 else
443 vnlist = (FVECT *)realloc((void *)vnlist,
444 (nvns+CHUNKSIZ)*sizeof(FVECT));
445 if (vnlist == NULL)
446 error(SYSTEM, "out of memory in newvn");
447 }
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 static int
459 newvt( /* create a new texture map vertex */
460 double x,
461 double y
462 )
463 {
464 if (!(nvts%CHUNKSIZ)) { /* allocate next block */
465 if (nvts == 0)
466 vtlist = (RREAL (*)[2])malloc(CHUNKSIZ*2*sizeof(RREAL));
467 else
468 vtlist = (RREAL (*)[2])realloc((void *)vtlist,
469 (nvts+CHUNKSIZ)*2*sizeof(RREAL));
470 if (vtlist == NULL)
471 error(SYSTEM, "out of memory in newvt");
472 }
473 /* assign new vertex */
474 vtlist[nvts][0] = x;
475 vtlist[nvts][1] = y;
476 return(++nvts);
477 }
478
479
480 static void
481 syntax( /* report syntax error and exit */
482 char *er
483 )
484 {
485 sprintf(errmsg, "%s: Wavefront syntax error near line %d: %s",
486 inpfile, lineno, er);
487 error(USER, errmsg);
488 }