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