ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/obj2rad.c
Revision: 2.17
Committed: Sat Feb 22 02:07:23 2003 UTC (21 years, 1 month ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.16: +4 -7 lines
Log Message:
Changes and check-in for 3.5 release
Includes new source files and modifications not recorded for many years
See ray/doc/notes/ReleaseNotes for notes between 3.1 and 3.5 release

File Contents

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