ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/obj2rad.c
Revision: 2.23
Committed: Fri Apr 23 16:20:56 2004 UTC (19 years, 11 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad3R7P2, rad3R7P1, rad3R6, rad3R6P1, rad3R8, rad3R9
Changes since 2.22: +11 -6 lines
Log Message:
Increased .OBJ face vertex limit to 512 (was 64)

File Contents

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