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