ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/obj2rad.c
Revision: 2.16
Committed: Wed Jul 24 13:07:41 1996 UTC (27 years, 9 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.15: +16 -1 lines
Log Message:
added check for flat triangles with normals

File Contents

# Content
1 /* Copyright (c) 1994 Regents of the University of California */
2
3 #ifndef lint
4 static char SCCSid[] = "$SunId$ LBL";
5 #endif
6
7 /*
8 * Convert a Wavefront .obj file to Radiance format.
9 *
10 * Currently, we support only polygonal geometry. Non-planar
11 * faces are broken rather haphazardly into triangles.
12 * Also, texture map indices only work for triangles, though
13 * I'm not sure they work correctly. (Taken out -- see TEXMAPS defines.)
14 */
15
16 #include "standard.h"
17
18 #include "trans.h"
19
20 #include "tmesh.h"
21
22 #include <ctype.h>
23
24 #define PATNAME "M-pat" /* mesh pattern name (reused) */
25 #define TEXNAME "M-nor" /* mesh texture name (reused) */
26 #define DEFOBJ "unnamed" /* default object name */
27 #define DEFMAT "white" /* default material name */
28
29 #define pvect(v) printf("%18.12g %18.12g %18.12g\n",(v)[0],(v)[1],(v)[2])
30
31 FVECT *vlist; /* our vertex list */
32 int nvs; /* number of vertices in our list */
33 FVECT *vnlist; /* vertex normal list */
34 int nvns;
35 FLOAT (*vtlist)[2]; /* map vertex list */
36 int nvts;
37
38 typedef int VNDX[3]; /* vertex index (point,map,normal) */
39
40 #define CHUNKSIZ 256 /* vertex allocation chunk size */
41
42 #define MAXARG 64 /* maximum # arguments in a statement */
43
44 /* qualifiers */
45 #define Q_MTL 0
46 #define Q_MAP 1
47 #define Q_GRP 2
48 #define Q_OBJ 3
49 #define Q_FAC 4
50 #define NQUALS 5
51
52 char *qname[NQUALS] = {
53 "Material",
54 "Map",
55 "Group",
56 "Object",
57 "Face",
58 };
59
60 QLIST qlist = {NQUALS, qname};
61 /* valid qualifier ids */
62 IDLIST qual[NQUALS];
63 /* mapping rules */
64 RULEHD *ourmapping = NULL;
65
66 char *defmat = DEFMAT; /* default (starting) material name */
67 char *defobj = DEFOBJ; /* default (starting) object name */
68
69 int flatten = 0; /* discard surface normal information */
70
71 char *getmtl(), *getonm();
72
73 char mapname[128]; /* current picture file */
74 char matname[64]; /* current material name */
75 char group[16][32]; /* current group names */
76 char objname[128]; /* current object name */
77 char *inpfile; /* input file name */
78 int lineno; /* current line number */
79 int faceno; /* current face number */
80
81
82 main(argc, argv) /* read in .obj file and convert */
83 int argc;
84 char *argv[];
85 {
86 int donames = 0;
87 int i;
88
89 for (i = 1; i < argc && argv[i][0] == '-'; i++)
90 switch (argv[i][1]) {
91 case 'o': /* object name */
92 defobj = argv[++i];
93 break;
94 case 'n': /* just produce name list */
95 donames++;
96 break;
97 case 'm': /* use custom mapfile */
98 ourmapping = getmapping(argv[++i], &qlist);
99 break;
100 case 'f': /* flatten surfaces */
101 flatten++;
102 break;
103 default:
104 goto userr;
105 }
106 if (i > argc | i < argc-1)
107 goto userr;
108 if (i == argc)
109 inpfile = "<stdin>";
110 else if (freopen(inpfile=argv[i], "r", stdin) == NULL) {
111 fprintf(stderr, "%s: cannot open\n", inpfile);
112 exit(1);
113 }
114 if (donames) { /* scan for ids */
115 getnames(stdin);
116 printf("filename \"%s\"\n", inpfile);
117 printf("filetype \"Wavefront\"\n");
118 write_quals(&qlist, qual, stdout);
119 printf("qualifier %s begin\n", qlist.qual[Q_FAC]);
120 printf("[%d:%d]\n", 1, faceno);
121 printf("end\n");
122 } else { /* translate file */
123 printf("# ");
124 printargs(argc, argv, stdout);
125 convert(stdin);
126 }
127 exit(0);
128 userr:
129 fprintf(stderr, "Usage: %s [-o obj][-m mapping][-n][-f] [file.obj]\n",
130 argv[0]);
131 exit(1);
132 }
133
134
135 getnames(fp) /* get valid qualifier names */
136 FILE *fp;
137 {
138 char *argv[MAXARG];
139 int argc;
140 ID tmpid;
141 register int i;
142
143 while (argc = getstmt(argv, fp))
144 switch (argv[0][0]) {
145 case 'f': /* face */
146 if (!argv[0][1])
147 faceno++;
148 break;
149 case 'u':
150 if (!strcmp(argv[0], "usemtl")) { /* material */
151 if (argc < 2)
152 break; /* not fatal */
153 tmpid.number = 0;
154 tmpid.name = argv[1];
155 findid(&qual[Q_MTL], &tmpid, 1);
156 } else if (!strcmp(argv[0], "usemap")) {/* map */
157 if (argc < 2 || !strcmp(argv[1], "off"))
158 break; /* not fatal */
159 tmpid.number = 0;
160 tmpid.name = argv[1];
161 findid(&qual[Q_MAP], &tmpid, 1);
162 }
163 break;
164 case 'o': /* object name */
165 if (argv[0][1] || argc < 2)
166 break;
167 tmpid.number = 0;
168 tmpid.name = argv[1];
169 findid(&qual[Q_OBJ], &tmpid, 1);
170 break;
171 case 'g': /* group name(s) */
172 if (argv[0][1])
173 break;
174 tmpid.number = 0;
175 for (i = 1; i < argc; i++) {
176 tmpid.name = argv[i];
177 findid(&qual[Q_GRP], &tmpid, 1);
178 }
179 break;
180 }
181 }
182
183
184 convert(fp) /* convert a T-mesh */
185 FILE *fp;
186 {
187 char *argv[MAXARG];
188 int argc;
189 int nstats, nunknown;
190 register int i;
191
192 nstats = nunknown = 0;
193 /* scan until EOF */
194 while (argc = getstmt(argv, fp)) {
195 switch (argv[0][0]) {
196 case 'v': /* vertex */
197 switch (argv[0][1]) {
198 case '\0': /* point */
199 if (badarg(argc-1,argv+1,"fff"))
200 syntax("Bad vertex");
201 newv(atof(argv[1]), atof(argv[2]),
202 atof(argv[3]));
203 break;
204 case 'n': /* normal */
205 if (argv[0][2])
206 goto unknown;
207 if (badarg(argc-1,argv+1,"fff"))
208 syntax("Bad normal");
209 if (!newvn(atof(argv[1]), atof(argv[2]),
210 atof(argv[3])))
211 syntax("Zero normal");
212 break;
213 case 't': /* texture map */
214 if (argv[0][2])
215 goto unknown;
216 if (badarg(argc-1,argv+1,"ff"))
217 goto unknown;
218 newvt(atof(argv[1]), atof(argv[2]));
219 break;
220 default:
221 goto unknown;
222 }
223 break;
224 case 'f': /* face */
225 if (argv[0][1])
226 goto unknown;
227 faceno++;
228 switch (argc-1) {
229 case 0: case 1: case 2:
230 syntax("Too few vertices");
231 break;
232 case 3:
233 if (!puttri(argv[1], argv[2], argv[3]))
234 syntax("Bad triangle");
235 break;
236 default:
237 if (!putface(argc-1, argv+1))
238 syntax("Bad face");
239 break;
240 }
241 break;
242 case 'u':
243 if (!strcmp(argv[0], "usemtl")) { /* material */
244 if (argc < 2)
245 break; /* not fatal */
246 strcpy(matname, argv[1]);
247 } else if (!strcmp(argv[0], "usemap")) {/* map */
248 if (argc < 2)
249 break; /* not fatal */
250 if (!strcmp(argv[1], "off"))
251 mapname[0] = '\0';
252 else
253 sprintf(mapname, "%s.pic", argv[1]);
254 } else
255 goto unknown;
256 break;
257 case 'o': /* object name */
258 if (argv[0][1])
259 goto unknown;
260 if (argc < 2)
261 break; /* not fatal */
262 strcpy(objname, argv[1]);
263 break;
264 case 'g': /* group name(s) */
265 if (argv[0][1])
266 goto unknown;
267 for (i = 1; i < argc; i++)
268 strcpy(group[i-1], argv[i]);
269 group[i-1][0] = '\0';
270 break;
271 case '#': /* comment */
272 printargs(argc, argv, stdout);
273 break;
274 default:; /* something we don't deal with */
275 unknown:
276 nunknown++;
277 break;
278 }
279 nstats++;
280 }
281 printf("\n# Done processing file: %s\n", inpfile);
282 printf("# %d lines, %d statements, %d unrecognized\n",
283 lineno, nstats, nunknown);
284 }
285
286
287 int
288 getstmt(av, fp) /* read the next statement from fp */
289 register char *av[MAXARG];
290 FILE *fp;
291 {
292 extern char *fgetline();
293 static char sbuf[MAXARG*10];
294 register char *cp;
295 register int i;
296
297 do {
298 if (fgetline(cp=sbuf, sizeof(sbuf), fp) == NULL)
299 return(0);
300 i = 0;
301 for ( ; ; ) {
302 while (isspace(*cp) || *cp == '\\') {
303 if (*cp == '\n')
304 lineno++;
305 *cp++ = '\0';
306 }
307 if (!*cp || i >= MAXARG-1)
308 break;
309 av[i++] = cp;
310 while (*++cp && !isspace(*cp))
311 ;
312 }
313 av[i] = NULL;
314 lineno++;
315 } while (!i);
316
317 return(i);
318 }
319
320
321 char *
322 getmtl() /* figure material for this face */
323 {
324 register RULEHD *rp = ourmapping;
325
326 if (rp == NULL) { /* no rule set */
327 if (matname[0])
328 return(matname);
329 if (group[0][0])
330 return(group[0]);
331 return(defmat);
332 }
333 /* check for match */
334 do {
335 if (matchrule(rp)) {
336 if (!strcmp(rp->mnam, VOIDID))
337 return(NULL); /* match is null */
338 return(rp->mnam);
339 }
340 rp = rp->next;
341 } while (rp != NULL);
342 /* no match found */
343 return(NULL);
344 }
345
346
347 char *
348 getonm() /* invent a good name for object */
349 {
350 static char name[64];
351 register char *cp1, *cp2;
352 register int i;
353 /* check for preset */
354 if (objname[0])
355 return(objname);
356 if (!group[0][0])
357 return(defobj);
358 cp1 = name; /* else make name out of groups */
359 for (i = 0; group[i][0]; i++) {
360 cp2 = group[i];
361 if (cp1 > name)
362 *cp1++ = '.';
363 while (*cp1 = *cp2++)
364 if (++cp1 >= name+sizeof(name)-2) {
365 *cp1 = '\0';
366 return(name);
367 }
368 }
369 return(name);
370 }
371
372
373 matchrule(rp) /* check for a match on this rule */
374 register RULEHD *rp;
375 {
376 ID tmpid;
377 int gotmatch;
378 register int i;
379
380 if (rp->qflg & FL(Q_MTL)) {
381 if (!matname[0])
382 return(0);
383 tmpid.number = 0;
384 tmpid.name = matname;
385 if (!matchid(&tmpid, &idm(rp)[Q_MTL]))
386 return(0);
387 }
388 if (rp->qflg & FL(Q_MAP)) {
389 if (!mapname[0])
390 return(0);
391 tmpid.number = 0;
392 tmpid.name = mapname;
393 if (!matchid(&tmpid, &idm(rp)[Q_MAP]))
394 return(0);
395 }
396 if (rp->qflg & FL(Q_GRP)) {
397 tmpid.number = 0;
398 gotmatch = 0;
399 for (i = 0; group[i][0]; i++) {
400 tmpid.name = group[i];
401 gotmatch |= matchid(&tmpid, &idm(rp)[Q_GRP]);
402 }
403 if (!gotmatch)
404 return(0);
405 }
406 if (rp->qflg & FL(Q_OBJ)) {
407 if (!objname[0])
408 return(0);
409 tmpid.number = 0;
410 tmpid.name = objname;
411 if (!matchid(&tmpid, &idm(rp)[Q_OBJ]))
412 return(0);
413 }
414 if (rp->qflg & FL(Q_FAC)) {
415 tmpid.name = NULL;
416 tmpid.number = faceno;
417 if (!matchid(&tmpid, &idm(rp)[Q_FAC]))
418 return(0);
419 }
420 return(1);
421 }
422
423
424 cvtndx(vi, vs) /* convert vertex string to index */
425 register VNDX vi;
426 register char *vs;
427 {
428 /* get point */
429 vi[0] = atoi(vs);
430 if (vi[0] > 0) {
431 if (vi[0]-- > nvs)
432 return(0);
433 } else if (vi[0] < 0) {
434 vi[0] += nvs;
435 if (vi[0] < 0)
436 return(0);
437 } else
438 return(0);
439 /* get map */
440 while (*vs)
441 if (*vs++ == '/')
442 break;
443 vi[1] = atoi(vs);
444 if (vi[1] > 0) {
445 if (vi[1]-- > nvts)
446 return(0);
447 } else if (vi[1] < 0) {
448 vi[1] += nvts;
449 if (vi[1] < 0)
450 return(0);
451 } else
452 vi[1] = -1;
453 /* get normal */
454 while (*vs)
455 if (*vs++ == '/')
456 break;
457 vi[2] = atoi(vs);
458 if (vi[2] > 0) {
459 if (vi[2]-- > nvns)
460 return(0);
461 } else if (vi[2] < 0) {
462 vi[2] += nvns;
463 if (vi[2] < 0)
464 return(0);
465 } else
466 vi[2] = -1;
467 return(1);
468 }
469
470
471 nonplanar(ac, av) /* are vertices non-planar? */
472 register int ac;
473 register char **av;
474 {
475 VNDX vi;
476 FLOAT *p0, *p1;
477 FVECT v1, v2, nsum, newn;
478 double d;
479 register int i;
480
481 if (!cvtndx(vi, av[0]))
482 return(0);
483 if (!flatten && vi[2] >= 0)
484 return(1); /* has interpolated normals */
485 if (ac < 4)
486 return(0); /* it's a triangle! */
487 /* set up */
488 p0 = vlist[vi[0]];
489 if (!cvtndx(vi, av[1]))
490 return(0); /* error gets caught later */
491 nsum[0] = nsum[1] = nsum[2] = 0.;
492 p1 = vlist[vi[0]];
493 fvsum(v2, p1, p0, -1.0);
494 for (i = 2; i < ac; i++) {
495 VCOPY(v1, v2);
496 if (!cvtndx(vi, av[i]))
497 return(0);
498 p1 = vlist[vi[0]];
499 fvsum(v2, p1, p0, -1.0);
500 fcross(newn, v1, v2);
501 if (normalize(newn) == 0.0) {
502 if (i < 3)
503 return(1); /* can't deal with this */
504 fvsum(nsum, nsum, nsum, 1./(i-2));
505 continue;
506 }
507 d = fdot(newn,nsum);
508 if (d >= 0) {
509 if (d < (1.0-FTINY)*(i-2))
510 return(1);
511 fvsum(nsum, nsum, newn, 1.0);
512 } else {
513 if (d > -(1.0-FTINY)*(i-2))
514 return(1);
515 fvsum(nsum, nsum, newn, -1.0);
516 }
517 }
518 return(0);
519 }
520
521
522 putface(ac, av) /* put out an N-sided polygon */
523 int ac;
524 register char **av;
525 {
526 VNDX vi;
527 char *cp;
528 register int i;
529
530 if (nonplanar(ac, av)) { /* break into triangles */
531 while (ac > 2) {
532 if (!puttri(av[0], av[1], av[2]))
533 return(0);
534 ac--; /* remove vertex & rotate */
535 cp = av[0];
536 for (i = 0; i < ac-1; i++)
537 av[i] = av[i+2];
538 av[i] = cp;
539 }
540 return(1);
541 }
542 if ((cp = getmtl()) == NULL)
543 return(-1);
544 printf("\n%s polygon %s.%d\n", cp, getonm(), faceno);
545 printf("0\n0\n%d\n", 3*ac);
546 for (i = 0; i < ac; i++) {
547 if (!cvtndx(vi, av[i]))
548 return(0);
549 pvect(vlist[vi[0]]);
550 }
551 return(1);
552 }
553
554
555 puttri(v1, v2, v3) /* put out a triangle */
556 char *v1, *v2, *v3;
557 {
558 char *mod;
559 VNDX v1i, v2i, v3i;
560 BARYCCM bvecs;
561 FLOAT bcoor[3][3];
562 int texOK, patOK;
563 register int i;
564
565 if ((mod = getmtl()) == NULL)
566 return(-1);
567
568 if (!cvtndx(v1i, v1) || !cvtndx(v2i, v2) || !cvtndx(v3i, v3))
569 return(0);
570 /* compute barycentric coordinates */
571 if (!flatten && v1i[2]>=0 && v2i[2]>=0 && v3i[2]>=0)
572 switch (flat_tri(vlist[v1i[0]], vlist[v2i[0]], vlist[v3i[0]],
573 vnlist[v1i[2]], vnlist[v2i[2]], vnlist[v3i[2]])) {
574 case DEGEN: /* zero area */
575 return(-1);
576 case RVFLAT: /* reversed normals, but flat */
577 case ISFLAT: /* smoothing unnecessary */
578 texOK = 0;
579 break;
580 case RVBENT: /* reversed normals with smoothing */
581 case ISBENT: /* proper smoothing */
582 texOK = 1;
583 break;
584 }
585 else
586 texOK = 0;
587 #ifdef TEXMAPS
588 patOK = mapname[0] && (v1i[1]>=0 && v2i[1]>=0 && v3i[1]>=0);
589 #else
590 patOK = 0;
591 #endif
592 if (texOK | patOK)
593 if (comp_baryc(&bvecs, vlist[v1i[0]], vlist[v2i[0]],
594 vlist[v3i[0]]) < 0)
595 return(-1);
596 /* put out texture (if any) */
597 if (texOK) {
598 printf("\n%s texfunc %s\n", mod, TEXNAME);
599 mod = TEXNAME;
600 printf("4 dx dy dz %s\n", TCALNAME);
601 printf("0\n");
602 for (i = 0; i < 3; i++) {
603 bcoor[i][0] = vnlist[v1i[2]][i];
604 bcoor[i][1] = vnlist[v2i[2]][i];
605 bcoor[i][2] = vnlist[v3i[2]][i];
606 }
607 put_baryc(&bvecs, bcoor, 3);
608 }
609 #ifdef TEXMAPS
610 /* put out pattern (if any) */
611 if (patOK) {
612 printf("\n%s colorpict %s\n", mod, PATNAME);
613 mod = PATNAME;
614 printf("7 noneg noneg noneg %s %s u v\n", mapname, TCALNAME);
615 printf("0\n");
616 for (i = 0; i < 2; i++) {
617 bcoor[i][0] = vtlist[v1i[1]][i];
618 bcoor[i][1] = vtlist[v2i[1]][i];
619 bcoor[i][2] = vtlist[v3i[1]][i];
620 }
621 put_baryc(&bvecs, bcoor, 2);
622 }
623 #endif
624 /* put out triangle */
625 printf("\n%s polygon %s.%d\n", mod, getonm(), faceno);
626 printf("0\n0\n9\n");
627 pvect(vlist[v1i[0]]);
628 pvect(vlist[v2i[0]]);
629 pvect(vlist[v3i[0]]);
630
631 return(1);
632 }
633
634
635 freeverts() /* free all vertices */
636 {
637 if (nvs) {
638 free((char *)vlist);
639 nvs = 0;
640 }
641 if (nvts) {
642 free((char *)vtlist);
643 nvts = 0;
644 }
645 if (nvns) {
646 free((char *)vnlist);
647 nvns = 0;
648 }
649 }
650
651
652 int
653 newv(x, y, z) /* create a new vertex */
654 double x, y, z;
655 {
656 if (!(nvs%CHUNKSIZ)) { /* allocate next block */
657 if (nvs == 0)
658 vlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT));
659 else
660 vlist = (FVECT *)realloc((char *)vlist,
661 (nvs+CHUNKSIZ)*sizeof(FVECT));
662 if (vlist == NULL) {
663 fprintf(stderr,
664 "Out of memory while allocating vertex %d\n", nvs);
665 exit(1);
666 }
667 }
668 /* assign new vertex */
669 vlist[nvs][0] = x;
670 vlist[nvs][1] = y;
671 vlist[nvs][2] = z;
672 return(++nvs);
673 }
674
675
676 int
677 newvn(x, y, z) /* create a new vertex normal */
678 double x, y, z;
679 {
680 if (!(nvns%CHUNKSIZ)) { /* allocate next block */
681 if (nvns == 0)
682 vnlist = (FVECT *)malloc(CHUNKSIZ*sizeof(FVECT));
683 else
684 vnlist = (FVECT *)realloc((char *)vnlist,
685 (nvns+CHUNKSIZ)*sizeof(FVECT));
686 if (vnlist == NULL) {
687 fprintf(stderr,
688 "Out of memory while allocating normal %d\n", nvns);
689 exit(1);
690 }
691 }
692 /* assign new normal */
693 vnlist[nvns][0] = x;
694 vnlist[nvns][1] = y;
695 vnlist[nvns][2] = z;
696 if (normalize(vnlist[nvns]) == 0.0)
697 return(0);
698 return(++nvns);
699 }
700
701
702 int
703 newvt(x, y) /* create a new texture map vertex */
704 double x, y;
705 {
706 if (!(nvts%CHUNKSIZ)) { /* allocate next block */
707 if (nvts == 0)
708 vtlist = (FLOAT (*)[2])malloc(CHUNKSIZ*2*sizeof(FLOAT));
709 else
710 vtlist = (FLOAT (*)[2])realloc((char *)vtlist,
711 (nvts+CHUNKSIZ)*2*sizeof(FLOAT));
712 if (vtlist == NULL) {
713 fprintf(stderr,
714 "Out of memory while allocating texture vertex %d\n",
715 nvts);
716 exit(1);
717 }
718 }
719 /* assign new vertex */
720 vtlist[nvts][0] = x;
721 vtlist[nvts][1] = y;
722 return(++nvts);
723 }
724
725
726 syntax(er) /* report syntax error and exit */
727 char *er;
728 {
729 fprintf(stderr, "%s: Wavefront syntax error near line %d: %s\n",
730 inpfile, lineno, er);
731 exit(1);
732 }