ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/cv/obj2rad.c
Revision: 2.29
Committed: Fri Nov 8 16:49:04 2013 UTC (10 years, 4 months ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: rad5R2, rad4R2P2, rad5R0, rad5R1, rad4R2, rad4R2P1
Changes since 2.28: +4 -4 lines
Log Message:
Increased maximum identifier name length from 64 to 256

File Contents

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