ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/obj2rad.c
Revision: 2.25
Committed: Mon Nov 10 19:08:18 2008 UTC (15 years, 5 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad4R0
Changes since 2.24: +2 -2 lines
Log Message:
Changed ".pic" extension to ".hdr" throughout

File Contents

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