]> git.mxchange.org Git - flightgear.git/blob - Tri2obj/tri2obj.c
e44f5559d5613bcd67de9dd3fc429186f7ca1a69
[flightgear.git] / Tri2obj / tri2obj.c
1 /* tri2obj.c -- read in a .ele/.node file pair generated by the triangle 
2  *              program and output a simple Wavefront .obj file.
3  *
4  * Written by Curtis Olson, started October 1997.
5  *
6  * Copyright (C) 1997  Curtis L. Olson  - curt@infoplane.com
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * $Id$
23  * (Log is kept at end of this file)
24  */
25
26
27 #include <stdio.h>
28 #include <stdlib.h>   /* for atoi() */
29 #include <string.h>
30 #include <sys/stat.h> /* for stat() */
31 #include <unistd.h>   /* for stat() */
32
33 #include "tri2obj.h"
34
35 #include <Include/fg_constants.h>
36 #include <Include/fg_types.h>
37 #include <Math/fg_geodesy.h>
38 #include <Math/mat3.h>
39 #include <Math/polar.h>
40 #include <Scenery/bucketutils.h>
41
42
43 int nodecount, tricount;
44 int normalcount = 0;
45 static struct fgCartesianPoint nodes[MAX_NODES];
46 static int tris[MAX_TRIS][3];
47
48 static double normals[MAX_NODES][3];
49
50 struct fgBUCKET my_index;
51 struct fgBUCKET ne_index, nw_index, sw_index, se_index;
52 struct fgBUCKET north_index, south_index, east_index, west_index;
53
54 /* convert a geodetic point lon(arcsec), lat(arcsec), elev(meter) to
55  * a cartesian point */
56 struct fgCartesianPoint geod_to_cart(double geod[3]) {
57     struct fgCartesianPoint p;
58     double gc_lon, gc_lat, sl_radius;
59
60     /* printf("A geodetic point is (%.2f, %.2f, %.2f)\n", 
61            geod[0], geod[1], geod[2]); */
62
63     gc_lon = geod[0]*ARCSEC_TO_RAD;
64     fgGeodToGeoc(geod[1]*ARCSEC_TO_RAD, geod[2], &sl_radius, &gc_lat);
65
66     /* printf("A geocentric point is (%.2f, %.2f, %.2f)\n", gc_lon, 
67            gc_lat, sl_radius+geod[2]); */
68
69     p = fgPolarToCart(gc_lon, gc_lat, sl_radius+geod[2]);
70     
71     /* printf("A cart point is (%.8f, %.8f, %.8f)\n", p.x, p.y, p.z); */
72
73     return(p);
74 }
75
76
77 /* given three points defining a triangle, calculate the normal */
78 void calc_normal(struct fgCartesianPoint p1, struct fgCartesianPoint p2, 
79                  struct fgCartesianPoint p3, double normal[3])
80 {
81     double v1[3], v2[3];
82     double temp;
83
84     v1[0] = p2.x - p1.x; v1[1] = p2.y - p1.y; v1[2] = p2.z - p1.z;
85     v2[0] = p3.x - p1.x; v2[1] = p3.y - p1.y; v2[2] = p3.z - p1.z;
86
87     MAT3cross_product(normal, v1, v2);
88     MAT3_NORMALIZE_VEC(normal, temp);
89
90 /*  printf("  Normal = %.2f %.2f %.2f\n", normal[0], normal[1], normal[2]); */
91 }
92
93
94 /* return the index of all triangles containing the specified node */
95 void find_tris(int n, int *t1, int *t2, int *t3, int *t4, int *t5) {
96     int i;
97
98     *t1 = *t2 = *t3 = *t4 = *t5 = 0;
99
100     i = 1;
101     while ( i <= tricount ) {
102         if ( (n == tris[i][0]) || (n == tris[i][1]) || (n == tris[i][2]) ) {
103             if ( *t1 == 0 ) {
104                 *t1 = i;
105             } else if ( *t2 == 0 ) {
106                 *t2 = i;
107             } else if ( *t3 == 0 ) {
108                 *t3 = i;
109             } else if ( *t4 == 0 ) {
110                 *t4 = i;
111             } else {
112                 *t5 = i;
113             }
114         }
115         i++;
116     }
117 }
118
119
120 /* return the file base name ( foo/bar/file.ext = file.ext ) */
121 void extract_file(char *in, char *base) {
122     int len, i;
123
124     len = strlen(in);
125
126     i = len - 1;
127     while ( (i >= 0) && (in[i] != '/') ) {
128         i--;
129     }
130
131     in += (i + 1);
132     strcpy(base, in);
133 }
134
135
136 /* return the file path name ( foo/bar/file.ext = foo/bar ) */
137 void extract_path(char *in, char *base) {
138     int len, i;
139
140     len = strlen(in);
141     strcpy(base, in);
142
143     i = len - 1;
144     while ( (i >= 0) && (in[i] != '/') ) {
145         i--;
146     }
147
148     base[i] = '\0';
149 }
150
151
152 /* check if a file exists */
153 int file_exists(char *file) {
154     struct stat stat_buf;
155     int result;
156
157     printf("checking %s ... ", file);
158
159     result = stat(file, &stat_buf);
160
161     if ( result != 0 ) {
162         /* stat failed, no file */
163         printf("not found.\n");
164         return(0);
165     } else {
166         /* stat succeeded, file exists */
167         printf("exists.\n");
168         return(1);
169     }
170 }
171
172
173 /* check to see if a shared object exists */
174 int shared_object_exists(char *basepath, char *ext, char *file) {
175     char scene_path[256];
176     long int index;
177
178     if ( strcmp(ext, ".sw") == 0 ) {
179         fgBucketGenBasePath(&my_index, scene_path);
180         index = fgBucketGenIndex(&my_index);
181         sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index);
182         if ( file_exists(file) ) {
183             return(1);
184         }
185         fgBucketGenBasePath(&west_index, scene_path);
186         index = fgBucketGenIndex(&west_index);
187         sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index);
188         if ( file_exists(file) ) {
189             return(1);
190         }
191         fgBucketGenBasePath(&sw_index, scene_path);
192         index = fgBucketGenIndex(&sw_index);
193         sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index);
194         if ( file_exists(file) ) {
195             return(1);
196         }
197         fgBucketGenBasePath(&south_index, scene_path);
198         index = fgBucketGenIndex(&south_index);
199         sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index);
200         if ( file_exists(file) ) {
201             return(1);
202         }
203     }
204
205     if ( strcmp(ext, ".se") == 0 ) {
206         fgBucketGenBasePath(&my_index, scene_path);
207         index = fgBucketGenIndex(&my_index);
208         sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index);
209         if ( file_exists(file) ) {
210             return(1);
211         }
212         fgBucketGenBasePath(&east_index, scene_path);
213         index = fgBucketGenIndex(&east_index);
214         sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index);
215         if ( file_exists(file) ) {
216             return(1);
217         }
218         fgBucketGenBasePath(&se_index, scene_path);
219         index = fgBucketGenIndex(&se_index);
220         sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index);
221         if ( file_exists(file) ) {
222             return(1);
223         }
224         fgBucketGenBasePath(&south_index, scene_path);
225         index = fgBucketGenIndex(&south_index);
226         sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index);
227         if ( file_exists(file) ) {
228             return(1);
229         }
230     }
231
232     if ( strcmp(ext, ".ne") == 0 ) {
233         fgBucketGenBasePath(&my_index, scene_path);
234         index = fgBucketGenIndex(&my_index);
235         sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index);
236         if ( file_exists(file) ) {
237             return(1);
238         }
239         fgBucketGenBasePath(&east_index, scene_path);
240         index = fgBucketGenIndex(&east_index);
241         sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index);
242         if ( file_exists(file) ) {
243             return(1);
244         }
245         fgBucketGenBasePath(&ne_index, scene_path);
246         index = fgBucketGenIndex(&ne_index);
247         sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index);
248         if ( file_exists(file) ) {
249             return(1);
250         }
251         fgBucketGenBasePath(&north_index, scene_path);
252         index = fgBucketGenIndex(&north_index);
253         sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index);
254         if ( file_exists(file) ) {
255             return(1);
256         }
257     }
258
259     if ( strcmp(ext, ".nw") == 0 ) {
260         fgBucketGenBasePath(&my_index, scene_path);
261         index = fgBucketGenIndex(&my_index);
262         sprintf(file, "%s/%s/%ld.1.nw", basepath, scene_path, index);
263         if ( file_exists(file) ) {
264             return(1);
265         }
266         fgBucketGenBasePath(&west_index, scene_path);
267         index = fgBucketGenIndex(&west_index);
268         sprintf(file, "%s/%s/%ld.1.ne", basepath, scene_path, index);
269         if ( file_exists(file) ) {
270             return(1);
271         }
272         fgBucketGenBasePath(&nw_index, scene_path);
273         index = fgBucketGenIndex(&nw_index);
274         sprintf(file, "%s/%s/%ld.1.se", basepath, scene_path, index);
275         if ( file_exists(file) ) {
276             return(1);
277         }
278         fgBucketGenBasePath(&north_index, scene_path);
279         index = fgBucketGenIndex(&north_index);
280         sprintf(file, "%s/%s/%ld.1.sw", basepath, scene_path, index);
281         if ( file_exists(file) ) {
282             return(1);
283         }
284     }
285
286     if ( strcmp(ext, ".south") == 0 ) {
287         fgBucketGenBasePath(&my_index, scene_path);
288         index = fgBucketGenIndex(&my_index);
289         sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index);
290         if ( file_exists(file) ) {
291             return(1);
292         }
293         fgBucketGenBasePath(&south_index, scene_path);
294         index = fgBucketGenIndex(&south_index);
295         sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index);
296         if ( file_exists(file) ) {
297             return(1);
298         }
299     }
300
301     if ( strcmp(ext, ".north") == 0 ) {
302         fgBucketGenBasePath(&my_index, scene_path);
303         index = fgBucketGenIndex(&my_index);
304         sprintf(file, "%s/%s/%ld.1.north", basepath, scene_path, index);
305         if ( file_exists(file) ) {
306             return(1);
307         }
308         fgBucketGenBasePath(&north_index, scene_path);
309         index = fgBucketGenIndex(&north_index);
310         sprintf(file, "%s/%s/%ld.1.south", basepath, scene_path, index);
311         if ( file_exists(file) ) {
312             return(1);
313         }
314     }
315
316     if ( strcmp(ext, ".west") == 0 ) {
317         fgBucketGenBasePath(&my_index, scene_path);
318         index = fgBucketGenIndex(&my_index);
319         sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index);
320         if ( file_exists(file) ) {
321             return(1);
322         }
323         fgBucketGenBasePath(&west_index, scene_path);
324         index = fgBucketGenIndex(&west_index);
325         sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index);
326         if ( file_exists(file) ) {
327             return(1);
328         }
329     }
330
331     if ( strcmp(ext, ".east") == 0 ) {
332         fgBucketGenBasePath(&my_index, scene_path);
333         index = fgBucketGenIndex(&my_index);
334         sprintf(file, "%s/%s/%ld.1.east", basepath, scene_path, index);
335         if ( file_exists(file) ) {
336             return(1);
337         }
338         fgBucketGenBasePath(&east_index, scene_path);
339         index = fgBucketGenIndex(&east_index);
340         sprintf(file, "%s/%s/%ld.1.west", basepath, scene_path, index);
341         if ( file_exists(file) ) {
342             return(1);
343         }
344     }
345
346     if ( strcmp(ext, ".body") == 0 ) {
347         fgBucketGenBasePath(&my_index, scene_path);
348         index = fgBucketGenIndex(&my_index);
349         sprintf(file, "%s/%s/%ld.1.body", basepath, scene_path, index);
350         if ( file_exists(file) ) {
351             return(1);
352         }
353     }
354
355     return(0);
356 }
357
358
359 /* given a file pointer, read all the vn (normals from it) */
360 void read_normals(FILE *fp) {
361     char line[256];
362
363     while ( fgets(line, 250, fp) != NULL ) {
364         if ( strncmp(line, "vn ", 3) == 0 ) {
365             sscanf( line, "vn %lf %lf %lf\n", 
366                     &normals[normalcount][0], 
367                     &normals[normalcount][1], 
368                     &normals[normalcount][2] );
369             /*
370             printf("read_normals(%d) %.2f %.2f %.2f %s", normalcount, 
371                    normals[normalcount][0], normals[normalcount][1], 
372                    normals[normalcount][2], line);
373                    */
374             normalcount++;
375         }
376     }
377 }
378
379
380 /* my custom file opening routine ... don't open if a shared edge or
381  * vertex alread exists */
382 FILE *my_open(char *basename, char *basepath, char *ext) {
383     FILE *fp;
384     char filename[256];
385
386     /* check if a shared object already exists */
387     if ( shared_object_exists(basepath, ext, filename) ) {
388         /* not an actual file open error, but we've already got the
389          * shared edge, so we don't want to create another one */
390         fp = fopen(filename, "r");
391         printf("Opening %s\n", filename);
392         return(fp);
393     } else {
394         /* open the file */
395         printf("not opening\n");
396         return(NULL);
397     }
398 }
399
400
401 /* Initialize a new mesh structure */
402 void triload(char *basename, char *basepath) {
403     char nodename[256], elename[256];
404     double n[3];
405     FILE *ne, *nw, *se, *sw, *north, *south, *east, *west;
406     FILE *node, *ele;
407     int dim, junk1, junk2;
408     int i;
409
410     ne = my_open(basename, basepath, ".ne");
411     read_normals(ne);
412     fclose(ne);
413
414     nw = my_open(basename, basepath, ".nw");
415     read_normals(nw);
416     fclose(nw);
417
418     se = my_open(basename, basepath, ".se");
419     read_normals(se);
420     fclose(se);
421
422     sw = my_open(basename, basepath, ".sw");
423     read_normals(sw);
424     fclose(sw);
425
426     north = my_open(basename, basepath, ".north");
427     read_normals(north);
428     fclose(north);
429
430     south = my_open(basename, basepath, ".south");
431     read_normals(south);
432     fclose(south);
433
434     east = my_open(basename, basepath, ".east");
435     read_normals(east);
436     fclose(east);
437
438     west = my_open(basename, basepath, ".west");
439     read_normals(west);
440     fclose(west);
441
442     strcpy(nodename, basename);
443     strcat(nodename, ".node");
444     strcpy(elename, basename);
445     strcat(elename, ".ele");
446
447     printf("Loading node file:  %s ...\n", nodename);
448     if ( (node = fopen(nodename, "r")) == NULL ) {
449         printf("Cannot open file '%s'\n", nodename);
450         exit(-1);
451     }
452
453     fscanf(node, "%d %d %d %d", &nodecount, &dim, &junk1, &junk2);
454
455     if ( nodecount > MAX_NODES - 1 ) {
456         printf("Error, too many nodes, need to increase array size\n");
457         exit(-1);
458     } else {
459         printf("    Expecting %d nodes\n", nodecount);
460     }
461
462     for ( i = 1; i <= nodecount; i++ ) {
463         fscanf(node, "%d %lf %lf %lf %d\n", &junk1, 
464                &n[0], &n[1], &n[2], &junk2);
465         /* printf("%d %.2f %.2f %.2f\n", junk1, n[0], n[1], n[2]); */
466         nodes[i] = geod_to_cart(n);
467         /* printf("%d %.2f %.2f %.2f\n", 
468                junk1, nodes[i].x, nodes[i].y, nodes[i].z); */
469     }
470
471     fclose(node);
472
473     printf("Loading element file:  %s ...\n", elename);
474     if ( (ele = fopen(elename, "r")) == NULL ) {
475         printf("Cannot open file '%s'\n", elename);
476         exit(-1);
477     }
478
479     fscanf(ele, "%d %d %d", &tricount, &junk1, &junk2);
480
481     if ( tricount > MAX_TRIS - 1 ) {
482         printf("Error, too many elements, need to increase array size\n");
483         exit(-1);
484     } else {
485         printf("    Expecting %d elements\n", tricount);
486     }
487
488     for ( i = 1; i <= tricount; i++ ) {
489         fscanf(ele, "%d %d %d %d\n", &junk1, 
490                &tris[i][0], &tris[i][1], &tris[i][2]);
491         /* printf("%d %d %d %d\n", junk1, tris[i][0], tris[i][1], tris[i][2]);*/
492     }
493
494     fclose(ele);
495 }
496
497
498 /* dump in WaveFront .obj format */
499 void dump_obj(char *basename) {
500     char objname[256];
501     double n1[3], n2[3], n3[3], n4[3], n5[3], norm[3], temp;
502     FILE *obj;
503     int i, t1, t2, t3, t4, t5, count;
504
505     strcpy(objname, basename);
506     strcat(objname, ".obj");
507
508     printf("Dumping to file:  %s ...\n", objname);
509
510     obj = fopen(objname, "w");
511
512     /* dump vertices */
513     printf("  writing vertices\n");
514     for ( i = 1; i <= nodecount; i++ ) {
515         fprintf(obj, "v %.2f %.2f %.2f\n", 
516                 nodes[i].x, nodes[i].y, nodes[i].z);
517     }
518
519     printf("  calculating and writing normals\n");
520     printf("  First %d normals taken from shared files.\n", normalcount);
521                                                      
522     /* calculate and generate normals */
523     for ( i = 1; i <= nodecount; i++ ) {
524
525         if ( i <= normalcount ) {
526             /* use precalculated (shared) normal */
527             norm[0] = normals[i-1][0];
528             norm[1] = normals[i-1][1];
529             norm[2] = normals[i-1][2];
530         } else {
531             /* printf("Finding normal\n"); */
532
533             find_tris(i, &t1, &t2, &t3, &t4, &t5);
534
535             n1[0] = n1[1] = n1[2] = 0.0;
536             n2[0] = n2[1] = n2[2] = 0.0;
537             n3[0] = n3[1] = n3[2] = 0.0;
538             n4[0] = n4[1] = n4[2] = 0.0;
539             n5[0] = n5[1] = n5[2] = 0.0;
540
541             count = 1;
542             calc_normal(nodes[tris[t1][0]], nodes[tris[t1][1]], 
543                         nodes[tris[t1][2]], n1);
544
545             if ( t2 > 0 ) {
546                 calc_normal(nodes[tris[t2][0]], nodes[tris[t2][1]], 
547                             nodes[tris[t2][2]], n2);
548                 count = 2;
549             }
550
551             if ( t3 > 0 ) {
552                 calc_normal(nodes[tris[t3][0]], nodes[tris[t3][1]],
553                             nodes[tris[t3][2]], n3);
554                 count = 3;
555             }
556
557             if ( t4 > 0 ) {
558                 calc_normal(nodes[tris[t4][0]], nodes[tris[t4][1]],
559                             nodes[tris[t4][2]], n4);
560                 count = 4;
561             }
562
563             if ( t5 > 0 ) {
564                 calc_normal(nodes[tris[t5][0]], nodes[tris[t5][1]],
565                             nodes[tris[t5][2]], n5);
566                 count = 5;
567             }
568
569             /* printf("  norm[2] = %.2f %.2f %.2f\n", n1[2], n2[2], n3[2]); */
570
571             norm[0] = ( n1[0] + n2[0] + n3[0] + n4[0] + n5[0] ) / (double)count;
572             norm[1] = ( n1[1] + n2[1] + n3[1] + n4[1] + n5[1] ) / (double)count;
573             norm[2] = ( n1[2] + n2[2] + n3[2] + n4[2] + n5[2] ) / (double)count;
574         
575             /*  printf("  count = %d\n", count); */
576             /*  printf("  Ave. normal = %.4f %.4f %.4f\n", 
577                        norm[0], norm[1], norm[2]);*/
578             MAT3_NORMALIZE_VEC(norm, temp);
579             /*  printf("  Normalized ave. normal = %.4f %.4f %.4f\n",  */
580             /*         norm[0], norm[1], norm[2]); */
581         }
582         /* printf("%d vn %.4f %.4f %.4f\n", i, norm[0], norm[1], norm[2]); */
583         fprintf(obj, "vn %.4f %.4f %.4f\n", norm[0], norm[1], norm[2]);
584     }
585
586     /* dump faces */
587     printf("  writing faces\n");
588     for ( i = 1; i <= tricount; i++ ) {
589         fprintf(obj, "f %d %d %d\n", tris[i][0], tris[i][1], tris[i][2]);
590     }
591
592     fclose(obj);
593 }
594
595 int main(int argc, char **argv) {
596     char basename[256], basepath[256], temp[256];
597     long int tmp_index;
598     int len;
599
600     strcpy(basename, argv[1]);
601
602     /* find the base path of the file */
603     extract_path(basename, basepath);
604     extract_path(basepath, basepath);
605     extract_path(basepath, basepath);
606     printf("%s\n", basepath);
607
608     /* find the index of the current file */
609     extract_file(basename, temp);
610     len = strlen(temp);
611     if ( len >= 2 ) {
612         temp[len-2] = '\0';
613     }
614     tmp_index = atoi(temp);
615     printf("%ld\n", tmp_index);
616     fgBucketParseIndex(tmp_index, &my_index);
617
618     printf("bucket = %d %d %d %d\n", 
619            my_index.lon, my_index.lat, my_index.x, my_index.y);
620     /* generate the indexes of the neighbors */
621     fgBucketOffset(&my_index, &ne_index,  1,  1);
622     fgBucketOffset(&my_index, &nw_index, -1,  1);
623     fgBucketOffset(&my_index, &se_index,  1, -1);
624     fgBucketOffset(&my_index, &sw_index, -1, -1);
625
626     fgBucketOffset(&my_index, &north_index,  0,  1);
627     fgBucketOffset(&my_index, &south_index,  0, -1);
628     fgBucketOffset(&my_index, &east_index,  1,  0);
629     fgBucketOffset(&my_index, &west_index, -1,  0);
630
631     /* load the input data files */
632     triload(basename, basepath);
633
634     /* dump in WaveFront .obj format */
635     dump_obj(basename);
636
637     return(0);
638 }
639
640
641 /* $Log$
642 /* Revision 1.11  1998/03/03 16:01:00  curt
643 /* More c++ compile tweaks.
644 /*
645  * Revision 1.10  1998/01/31 00:41:27  curt
646  * Made a few changes converting floats to doubles.
647  *
648  * Revision 1.9  1998/01/27 18:37:04  curt
649  * Lots of updates to get back in sync with changes made over in .../Src/
650  *
651  * Revision 1.8  1998/01/17 01:25:39  curt
652  * Added support for shared normals.
653  *
654  * Revision 1.7  1998/01/12 02:42:00  curt
655  * Average up to five triangles per vertex instead of three.
656  *
657  * Revision 1.6  1998/01/09 23:03:15  curt
658  * Restructured to split 1deg x 1deg dem's into 64 subsections.
659  *
660  * Revision 1.5  1997/12/08 19:17:50  curt
661  * Fixed a type in the normal generation code.
662  *
663  * Revision 1.4  1997/12/02 13:13:32  curt
664  * Fixed problem with averaged vertex normals.
665  *
666  * Revision 1.3  1997/11/15 18:05:05  curt
667  * minor tweaks ...
668  *
669  * Revision 1.2  1997/11/14 00:29:13  curt
670  * Transform scenery coordinates at this point in pipeline when scenery is
671  * being translated to .obj format, not when it is being loaded into the end
672  * renderer.  Precalculate normals for each node as average of the normals
673  * of each containing polygon so Garoude shading is now supportable.
674  *
675  * Revision 1.1  1997/10/29 23:05:15  curt
676  * Initial revision.
677  *
678  */