ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/obj2rad.c
Revision: 2.32
Committed: Thu Apr 15 23:51:04 2021 UTC (3 years ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R4, HEAD
Changes since 2.31: +3 -3 lines
Log Message:
feat(genbox,robjutil): Added Radiance normal smoothing support to wfobj library

File Contents

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