]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_binobj.cxx
1cb467a00ee99ac8141c0f06e439f012056bb6b2
[simgear.git] / simgear / io / sg_binobj.cxx
1 // sg_binobj.cxx -- routines to read and write low level flightgear 3d objects
2 //
3 // Written by Curtis Olson, started January 2000.
4 //
5 // Copyright (C) 2000  Curtis L. Olson  - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22 //
23
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #include <stdio.h>
32 #include <time.h>
33 #include <zlib.h>
34
35 #include <list>
36 #include STL_STRING
37
38 #include <simgear/bucket/newbucket.hxx>
39
40 #include "lowlevel.hxx"
41 #include "sg_binobj.hxx"
42
43 #ifdef _MSC_VER
44 #  include <Win32/mkdir.hpp>
45 #endif
46
47 FG_USING_STD( cout );
48 FG_USING_STD( endl );
49
50 enum {
51     SG_BOUNDING_SPHERE = 0,
52
53     SG_VERTEX_LIST = 1,
54     SG_NORMAL_LIST = 2,
55     SG_TEXCOORD_LIST = 3,
56
57     SG_TRIANGLE_FACES = 10,
58     SG_TRIANGLE_STRIPS = 11,
59     SG_TRIANGLE_FANS = 12
60 } tgObjectTypes;
61
62 enum {
63     SG_MATERIAL = 0
64 } tgPropertyTypes;
65
66
67 // calculate the center of a list of points, by taking the halfway
68 // point between the min and max points.
69 static Point3D calc_center( point_list& wgs84_nodes ) {
70     Point3D p, min, max;
71
72     if ( wgs84_nodes.size() ) {
73         min = max = wgs84_nodes[0];
74     } else {
75         min = max = Point3D( 0 );
76     }
77
78     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
79         p = wgs84_nodes[i];
80
81         if ( p.x() < min.x() ) { min.setx( p.x() ); }
82         if ( p.y() < min.y() ) { min.sety( p.y() ); }
83         if ( p.z() < min.z() ) { min.setz( p.z() ); }
84
85         if ( p.x() > max.x() ) { max.setx( p.x() ); }
86         if ( p.y() > max.y() ) { max.sety( p.y() ); }
87         if ( p.z() > max.z() ) { max.setz( p.z() ); }
88     }
89
90     return ( min + max ) / 2.0;
91 }
92
93 // calculate the bounding sphere.  Center is the center of the
94 // tile and zero elevation
95 double sgCalcBoundingRadius( Point3D center, point_list& wgs84_nodes ) {
96     double dist_squared;
97     double radius_squared = 0;
98     
99     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
100         dist_squared = center.distance3Dsquared( wgs84_nodes[i] );
101         if ( dist_squared > radius_squared ) {
102             radius_squared = dist_squared;
103         }
104     }
105
106     return sqrt(radius_squared);
107 }
108
109
110 // read a binary file and populate the provided structures.
111 bool sgReadBinObj( const string& file, SGBinObject* obj ) {
112     Point3D p;
113     int i, j, k;
114     char material[256];
115
116     Point3D gbs_center = Point3D( 0 );
117     float gbs_radius = 0.0;
118     point_list wgs84_nodes;
119     point_list normals;
120     point_list texcoords;
121     group_list tris_v;
122     group_list tris_tc; 
123     string_list tri_materials;
124     group_list strips_v;
125     group_list strips_tc; 
126     string_list strip_materials;
127     group_list fans_v;
128     group_list fans_tc;
129     string_list fan_materials;
130
131     // zero out structures
132     wgs84_nodes.clear();
133     normals.clear();
134     texcoords.clear();
135
136     tris_v.clear();
137     tris_tc.clear();
138     tri_materials.clear();
139
140     strips_v.clear();
141     strips_tc.clear();
142     strip_materials.clear();
143
144     fans_v.clear();
145     fans_tc.clear();
146     fan_materials.clear();
147    
148     cout << "Loading binary input file = " << file << endl;
149
150     gzFile fp;
151     if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
152         string filegz = file + ".gz";
153         if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
154             cout << "ERROR: opening " << file << " or " << filegz
155                  << "for reading!" << endl;
156             return false;
157         }
158     }
159
160     sgClearReadError();
161
162     // read headers
163     int header, version;
164     sgReadInt( fp, &header );
165     if ( ((header & 0xFF000000) >> 24) == 'S' &&
166          ((header & 0x00FF0000) >> 16) == 'G' ) {
167         cout << "Good header" << endl;
168         // read file version
169         version = (header & 0x0000FFFF);
170         cout << "File version = " << version << endl;
171     } else {
172         return false;
173     }
174
175     // read creation time
176     time_t calendar_time;
177     sgReadLong( fp, &calendar_time );
178     struct tm *local_tm;
179     local_tm = localtime( &calendar_time );
180     char time_str[256];
181     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
182     cout << "File created on " << time_str << endl;
183
184     // read number of top level objects
185     short nobjects;
186     sgReadShort( fp, &nobjects );
187     cout << "Total objects to read = " << nobjects << endl;
188
189     // read in objects
190     for ( i = 0; i < nobjects; ++i ) {
191         // read object header
192         char obj_type;
193         short nproperties, nelements;
194         sgReadChar( fp, &obj_type );
195         sgReadShort( fp, &nproperties );
196         sgReadShort( fp, &nelements );
197
198         cout << "object " << i << " = " << (int)obj_type << " props = "
199              << nproperties << " elements = " << nelements << endl;
200             
201         if ( obj_type == SG_BOUNDING_SPHERE ) {
202             // read bounding sphere properties
203             for ( j = 0; j < nproperties; ++j ) {
204                 char prop_type;
205                 sgReadChar( fp, &prop_type );
206
207                 int nbytes;
208                 sgReadInt( fp, &nbytes );
209                 cout << "property size = " << nbytes << endl;
210                 char buf[nbytes];
211                 char *ptr = buf;
212                 sgReadBytes( fp, nbytes, ptr );
213             }
214
215             // read bounding sphere elements
216             for ( j = 0; j < nelements; ++j ) {
217                 int nbytes;
218                 sgReadInt( fp, &nbytes );
219                 cout << "element size = " << nbytes << endl;
220                 char buf[nbytes];
221                 char *ptr = buf;
222                 sgReadBytes( fp, nbytes, ptr );
223
224                 double *dptr = (double *)ptr;
225                 gbs_center = Point3D( dptr[0], dptr[1], dptr[2] );
226                 cout << "Center = " << gbs_center << endl;
227                 ptr += sizeof(double) * 3;
228                 
229                 float *fptr = (float *)ptr;
230                 gbs_radius = fptr[0];
231                 cout << "Bounding radius = " << gbs_radius << endl;
232             }
233         } else if ( obj_type == SG_VERTEX_LIST ) {
234             // read vertex list properties
235             for ( j = 0; j < nproperties; ++j ) {
236                 char prop_type;
237                 sgReadChar( fp, &prop_type );
238
239                 int nbytes;
240                 sgReadInt( fp, &nbytes );
241                 cout << "property size = " << nbytes << endl;
242                 char buf[nbytes];
243                 char *ptr = buf;
244                 sgReadBytes( fp, nbytes, ptr );
245             }
246
247             // read vertex list elements
248             for ( j = 0; j < nelements; ++j ) {
249                 int nbytes;
250                 sgReadInt( fp, &nbytes );
251                 cout << "element size = " << nbytes << endl;
252                 char buf[nbytes];
253                 char *ptr = buf;
254                 sgReadBytes( fp, nbytes, ptr );
255                 int count = nbytes / (sizeof(float) * 3);
256                 float *fptr = (float *)ptr;
257                 for ( k = 0; k < count; ++k ) {
258                     p = Point3D( fptr[0], fptr[1], fptr[2] );
259                     cout << "node = " << p << endl;
260                     wgs84_nodes.push_back( p );
261                     fptr += 3;
262                 }
263             }
264         } else if ( obj_type == SG_NORMAL_LIST ) {
265             // read normal list properties
266             for ( j = 0; j < nproperties; ++j ) {
267                 char prop_type;
268                 sgReadChar( fp, &prop_type );
269
270                 int nbytes;
271                 sgReadInt( fp, &nbytes );
272                 cout << "property size = " << nbytes << endl;
273                 char buf[nbytes];
274                 char *ptr = buf;
275                 sgReadBytes( fp, nbytes, ptr );
276             }
277
278             // read normal list elements
279             for ( j = 0; j < nelements; ++j ) {
280                 int nbytes;
281                 sgReadInt( fp, &nbytes );
282                 cout << "element size = " << nbytes << endl;
283                 char buf[nbytes];
284                 unsigned char *ptr = (unsigned char *)buf;
285                 sgReadBytes( fp, nbytes, ptr );
286                 int count = nbytes / 3;
287                 for ( k = 0; k < count; ++k ) {
288                     p = Point3D( ptr[0] / 128.0 - 1.0,
289                                  ptr[1] / 128.0 - 1.0,
290                                  ptr[2] / 128.0 - 1.0 );
291                     cout << "normal = " << p << endl;
292                     normals.push_back( p );
293                     ptr += 3;
294                 }
295             }
296         } else if ( obj_type == SG_TEXCOORD_LIST ) {
297             // read texcoord list properties
298             for ( j = 0; j < nproperties; ++j ) {
299                 char prop_type;
300                 sgReadChar( fp, &prop_type );
301
302                 int nbytes;
303                 sgReadInt( fp, &nbytes );
304                 cout << "property size = " << nbytes << endl;
305                 char buf[nbytes];
306                 char *ptr = buf;
307                 sgReadBytes( fp, nbytes, ptr );
308             }
309
310             // read texcoord list elements
311             for ( j = 0; j < nelements; ++j ) {
312                 int nbytes;
313                 sgReadInt( fp, &nbytes );
314                 cout << "element size = " << nbytes << endl;
315                 char buf[nbytes];
316                 char *ptr = buf;
317                 sgReadBytes( fp, nbytes, ptr );
318                 int count = nbytes / (sizeof(float) * 2);
319                 float *fptr = (float *)ptr;
320                 for ( k = 0; k < count; ++k ) {
321                     p = Point3D( fptr[0], fptr[1], 0 );
322                     cout << "texcoord = " << p << endl;
323                     texcoords.push_back( p );
324                     fptr += 2;
325                 }
326             }
327         } else if ( obj_type == SG_TRIANGLE_FACES ) {
328             // read triangle face properties
329             for ( j = 0; j < nproperties; ++j ) {
330                 char prop_type;
331                 sgReadChar( fp, &prop_type );
332
333                 int nbytes;
334                 sgReadInt( fp, &nbytes );
335                 cout << "property size = " << nbytes << endl;
336                 char buf[nbytes];
337                 char *ptr = buf;
338                 sgReadBytes( fp, nbytes, ptr );
339                 if ( prop_type == SG_MATERIAL ) {
340                     strncpy( material, ptr, nbytes );
341                     material[nbytes] = '\0';
342                     cout << "material type = " << material << endl;
343                 }
344             }
345
346             // read triangle face elements
347             for ( j = 0; j < nelements; ++j ) {
348                 int nbytes;
349                 sgReadInt( fp, &nbytes );
350                 cout << "element size = " << nbytes << endl;
351                 char buf[nbytes];
352                 char *ptr = buf;
353                 sgReadBytes( fp, nbytes, ptr );
354                 int count = nbytes / (sizeof(short) * 2);
355                 short *sptr = (short *)ptr;
356                 int_list vs, tcs;
357                 vs.clear(); tcs.clear();
358                 for ( k = 0; k < count; ++k ) {
359                     vs.push_back( sptr[0] );
360                     tcs.push_back( sptr[1] );
361                     cout << sptr[0] << "/" << sptr[1] << " ";
362                     sptr += 2;
363                 }
364                 cout << endl;
365                 tris_v.push_back( vs );
366                 tris_tc.push_back( tcs );
367                 tri_materials.push_back( material );
368             }
369         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
370             // read triangle strip properties
371             for ( j = 0; j < nproperties; ++j ) {
372                 char prop_type;
373                 sgReadChar( fp, &prop_type );
374
375                 int nbytes;
376                 sgReadInt( fp, &nbytes );
377                 cout << "property size = " << nbytes << endl;
378                 char buf[nbytes];
379                 char *ptr = buf;
380                 sgReadBytes( fp, nbytes, ptr );
381                 if ( prop_type == SG_MATERIAL ) {
382                     strncpy( material, ptr, nbytes );
383                     material[nbytes] = '\0';
384                     cout << "material type = " << material << endl;
385                 }
386             }
387
388             // read triangle strip elements
389             for ( j = 0; j < nelements; ++j ) {
390                 int nbytes;
391                 sgReadInt( fp, &nbytes );
392                 cout << "element size = " << nbytes << endl;
393                 char buf[nbytes];
394                 char *ptr = buf;
395                 sgReadBytes( fp, nbytes, ptr );
396                 int count = nbytes / (sizeof(short) * 2);
397                 short *sptr = (short *)ptr;
398                 int_list vs, tcs;
399                 vs.clear(); tcs.clear();
400                 for ( k = 0; k < count; ++k ) {
401                     vs.push_back( sptr[0] );
402                     tcs.push_back( sptr[1] );
403                     cout << sptr[0] << "/" << sptr[1] << " ";
404                     sptr += 2;
405                 }
406                 cout << endl;
407                 strips_v.push_back( vs );
408                 strips_tc.push_back( tcs );
409                 strip_materials.push_back( material );
410             }
411         } else if ( obj_type == SG_TRIANGLE_FANS ) {
412             // read triangle fan properties
413             for ( j = 0; j < nproperties; ++j ) {
414                 char prop_type;
415                 sgReadChar( fp, &prop_type );
416
417                 int nbytes;
418                 sgReadInt( fp, &nbytes );
419                 cout << "property size = " << nbytes << endl;
420                 char buf[nbytes];
421                 char *ptr = buf;
422                 sgReadBytes( fp, nbytes, ptr );
423                 if ( prop_type == SG_MATERIAL ) {
424                     strncpy( material, ptr, nbytes );
425                     material[nbytes] = '\0';
426                     cout << "material type = " << material << endl;
427                 }
428             }
429
430             // read triangle fan elements
431             for ( j = 0; j < nelements; ++j ) {
432                 int nbytes;
433                 sgReadInt( fp, &nbytes );
434                 cout << "element size = " << nbytes << endl;
435                 char buf[nbytes];
436                 char *ptr = buf;
437                 sgReadBytes( fp, nbytes, ptr );
438                 int count = nbytes / (sizeof(short) * 2);
439                 short *sptr = (short *)ptr;
440                 int_list vs, tcs;
441                 vs.clear(); tcs.clear();
442                 for ( k = 0; k < count; ++k ) {
443                     vs.push_back( sptr[0] );
444                     tcs.push_back( sptr[1] );
445                     cout << sptr[0] << "/" << sptr[1] << " ";
446                     sptr += 2;
447                 }
448                 cout << endl;
449                 fans_v.push_back( vs );
450                 fans_tc.push_back( tcs );
451                 fan_materials.push_back( material );
452             }
453         } else {
454             // unknown object type, just skip
455
456             // read properties
457             for ( j = 0; j < nproperties; ++j ) {
458                 char prop_type;
459                 sgReadChar( fp, &prop_type );
460
461                 int nbytes;
462                 sgReadInt( fp, &nbytes );
463                 cout << "property size = " << nbytes << endl;
464                 char buf[nbytes];
465                 char *ptr = buf;
466                 sgReadBytes( fp, nbytes, ptr );
467             }
468
469             // read elements
470             for ( j = 0; j < nelements; ++j ) {
471                 int nbytes;
472                 sgReadInt( fp, &nbytes );
473                 cout << "element size = " << nbytes << endl;
474                 char buf[nbytes];
475                 char *ptr = buf;
476                 sgReadBytes( fp, nbytes, ptr );
477             }
478         }
479     }
480
481     // close the file
482     gzclose(fp);
483
484     obj->set_gbs_center( gbs_center );
485     obj->set_gbs_radius( gbs_radius );
486     obj->set_wgs84_nodes( wgs84_nodes );
487     obj->set_normals( normals );
488     obj->set_texcoords( texcoords );
489     obj->set_tris_v( tris_v );
490     obj->set_tris_tc( tris_tc ); 
491     obj->set_tri_materials( tri_materials );
492     obj->set_strips_v( strips_v );
493     obj->set_strips_tc( strips_tc ); 
494     obj->set_strip_materials( strip_materials );
495     obj->set_fans_v( fans_v );
496     obj->set_fans_tc( fans_tc );
497     obj->set_fan_materials( fan_materials );
498
499     if ( sgReadError() ) {
500         cout << "We detected an error while reading the file." << endl;
501         return false;
502     }
503
504     return true;
505 }
506
507
508 // write out the structures to a binary file.  We assume that the
509 // groups come to us sorted by material property.  If not, things
510 // don't break, but the result won't be as optimal.
511 bool sgWriteBinObj( const string& base, const string& name, const SGBucket& b,
512                     const SGBinObject* obj ) 
513 {
514     Point3D p;
515     sgVec2 t;
516     sgVec3 pt;
517     int i, j;
518
519     Point3D gbs_center = obj->get_gbs_center();
520     float gbs_radius = obj->get_gbs_radius();
521     point_list wgs84_nodes = obj->get_wgs84_nodes();
522     point_list normals = obj->get_normals();
523     point_list texcoords = obj->get_texcoords();
524     group_list tris_v = obj->get_tris_v();
525     group_list tris_tc = obj->get_tris_tc(); 
526     string_list tri_materials = obj->get_tri_materials();
527     group_list strips_v = obj->get_strips_v();
528     group_list strips_tc = obj->get_strips_tc(); 
529     string_list strip_materials = obj->get_strip_materials();
530     group_list fans_v = obj->get_fans_v();
531     group_list fans_tc = obj->get_fans_tc();
532     string_list fan_materials = obj->get_fan_materials();
533
534     string dir = base + "/" + b.gen_base_path();
535     string command = "mkdir -p " + dir;
536 #ifdef _MSC_VER
537     fg_mkdir( dir.c_str() );
538 #else
539     system(command.c_str());
540 #endif
541
542     string file = dir + "/" + name + ".gz";
543     cout << "Output file = " << file << endl;
544
545     gzFile fp;
546     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
547         cout << "ERROR: opening " << file << " for writing!" << endl;
548         return false;
549     }
550
551     sgClearWriteError();
552
553     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
554          << tri_materials.size() << endl;
555     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
556          << strip_materials.size() << endl;
557     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
558          << fan_materials.size() << endl;
559
560     cout << "points = " << wgs84_nodes.size() << endl;
561     cout << "tex coords = " << texcoords.size() << endl;
562
563     // write header magic
564     sgWriteInt( fp, SG_FILE_MAGIC_NUMBER );
565     time_t calendar_time = time(NULL);
566     sgWriteLong( fp, (long int)calendar_time );
567
568     // calculate and write number of top level objects
569     string material;
570     int start;
571     int end;
572     short nobjects = 0;
573     nobjects++;                 // for gbs
574     nobjects++;                 // for vertices
575     nobjects++;                 // for normals
576     nobjects++;                 // for texcoords
577
578     // tris
579     short ntris = 0;
580     start = 0; end = 1;
581     while ( start < (int)tri_materials.size() ) {
582         material = tri_materials[start];
583         while ( (end < (int)tri_materials.size()) &&
584                 (material == tri_materials[end]) ) {
585             end++;
586         }
587         ntris++;
588         start = end; end = start + 1;
589     }
590     nobjects += ntris;
591
592     // strips
593     short nstrips = 0;
594     start = 0; end = 1;
595     while ( start < (int)strip_materials.size() ) {
596         material = strip_materials[start];
597         while ( (end < (int)strip_materials.size()) &&
598                 (material == strip_materials[end]) ) {
599             end++;
600         }
601         nstrips++;
602         start = end; end = start + 1;
603     }
604     nobjects += nstrips;
605
606     // fans
607     short nfans = 0;
608     start = 0; end = 1;
609     while ( start < (int)fan_materials.size() ) {
610         material = fan_materials[start];
611         while ( (end < (int)fan_materials.size()) &&
612                 (material == fan_materials[end]) ) {
613             end++;
614         }
615         nfans++;
616         start = end; end = start + 1;
617     }
618     nobjects += nfans;
619
620     cout << "total top level objects = " << nobjects << endl;
621     sgWriteShort( fp, nobjects );
622
623     // write bounding sphere
624     sgWriteChar( fp, (char)SG_BOUNDING_SPHERE );        // type
625     sgWriteShort( fp, 0 );                              // nproperties
626     sgWriteShort( fp, 1 );                              // nelements
627
628     sgWriteInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
629     sgdVec3 center;
630     sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
631     sgWritedVec3( fp, center );
632     sgWriteFloat( fp, gbs_radius );
633
634     // dump vertex list
635     sgWriteChar( fp, (char)SG_VERTEX_LIST );             // type
636     sgWriteShort( fp, 0 );                               // nproperties
637     sgWriteShort( fp, 1 );                               // nelements
638     sgWriteInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
639     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
640         p = wgs84_nodes[i] - gbs_center;
641         sgSetVec3( pt, p.x(), p.y(), p.z() );
642         sgWriteVec3( fp, pt );
643     }
644
645     // dump vertex normal list
646     sgWriteChar( fp, (char)SG_NORMAL_LIST );            // type
647     sgWriteShort( fp, 0 );                              // nproperties
648     sgWriteShort( fp, 1 );                              // nelements
649     sgWriteInt( fp, normals.size() * 3 );               // nbytes
650     char normal[3];
651     for ( i = 0; i < (int)normals.size(); ++i ) {
652         p = normals[i];
653         normal[0] = (char)((p.x() + 1.0) * 128);
654         normal[1] = (char)((p.y() + 1.0) * 128);
655         normal[2] = (char)((p.z() + 1.0) * 128);
656         sgWriteBytes( fp, 3, normal );
657     }
658
659     // dump texture coordinates
660     sgWriteChar( fp, (char)SG_TEXCOORD_LIST );          // type
661     sgWriteShort( fp, 0 );                              // nproperties
662     sgWriteShort( fp, 1 );                              // nelements
663     sgWriteInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
664     for ( i = 0; i < (int)texcoords.size(); ++i ) {
665         p = texcoords[i];
666         sgSetVec2( t, p.x(), p.y() );
667         sgWriteVec2( fp, t );
668     }
669
670     // dump individual triangles if they exist
671     if ( tris_v.size() > 0 ) {
672         int start = 0;
673         int end = 1;
674         string material;
675         while ( start < (int)tri_materials.size() ) {
676             // find next group
677             material = tri_materials[start];
678             while ( (end < (int)tri_materials.size()) && 
679                     (material == tri_materials[end]) )
680             {
681                 // cout << "end = " << end << endl;
682                 end++;
683             }
684             // cout << "group = " << start << " to " << end - 1 << endl;
685
686             // write group headers
687             sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
688             sgWriteShort( fp, 1 );                      // nproperties
689             sgWriteShort( fp, 1 );                      // nelements
690
691             sgWriteChar( fp, (char)SG_MATERIAL );       // property
692             sgWriteInt( fp, material.length() );        // nbytes
693             sgWriteBytes( fp, material.length(), material.c_str() );
694
695             sgWriteInt( fp, (end - start) * 3 * 2 * sizeof(short) ); // nbytes
696
697             // write group
698             for ( i = start; i < end; ++i ) {
699                 for ( j = 0; j < 3; ++j ) {
700                     sgWriteShort( fp, (short)tris_v[i][j] );
701                     sgWriteShort( fp, (short)tris_tc[i][j] );
702                 }
703             }
704
705             start = end;
706             end = start + 1;
707         }
708     }
709
710     // dump triangle strips
711     if ( strips_v.size() > 0 ) {
712         int start = 0;
713         int end = 1;
714         string material;
715         while ( start < (int)strip_materials.size() ) {
716             // find next group
717             material = strip_materials[start];
718             while ( (end < (int)strip_materials.size()) && 
719                     (material == strip_materials[end]) )
720                 {
721                     // cout << "end = " << end << endl;
722                     end++;
723                 }
724             // cout << "group = " << start << " to " << end - 1 << endl;
725
726             // write group headers
727             sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
728             sgWriteShort( fp, 1 );                       // nproperties
729             sgWriteShort( fp, end - start );             // nelements
730
731             sgWriteChar( fp, (char)SG_MATERIAL );       // property
732             sgWriteInt( fp, material.length() );        // nbytes
733             sgWriteBytes( fp, material.length(), material.c_str() );
734
735             // write strips
736             for ( i = start; i < end; ++i ) {
737                 // nbytes
738                 sgWriteInt( fp, strips_v[i].size() * 2 * sizeof(short) );
739                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
740                     sgWriteShort( fp, (short)strips_v[i][j] );
741                     sgWriteShort( fp, (short)strips_tc[i][j] );
742                 }
743             }
744             
745             start = end;
746             end = start + 1;
747         }
748     }
749
750     // dump triangle fans
751     if ( fans_v.size() > 0 ) {
752         int start = 0;
753         int end = 1;
754         string material;
755         while ( start < (int)fan_materials.size() ) {
756             // find next group
757             material = fan_materials[start];
758             while ( (end < (int)fan_materials.size()) && 
759                     (material == fan_materials[end]) )
760                 {
761                     // cout << "end = " << end << endl;
762                     end++;
763                 }
764             // cout << "group = " << start << " to " << end - 1 << endl;
765
766             // write group headers
767             sgWriteChar( fp, (char)SG_TRIANGLE_FANS );   // type
768             sgWriteShort( fp, 1 );                       // nproperties
769             sgWriteShort( fp, end - start );             // nelements
770
771             sgWriteChar( fp, (char)SG_MATERIAL );       // property
772             sgWriteInt( fp, material.length() );        // nbytes
773             sgWriteBytes( fp, material.length(), material.c_str() );
774
775             // write fans
776             for ( i = start; i < end; ++i ) {
777                 // nbytes
778                 sgWriteInt( fp, fans_v[i].size() * 2 * sizeof(short) );
779                 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
780                     sgWriteShort( fp, (short)fans_v[i][j] );
781                     sgWriteShort( fp, (short)fans_tc[i][j] );
782                 }
783             }
784             
785             start = end;
786             end = start + 1;
787         }
788     }
789
790     // close the file
791     gzclose(fp);
792
793     if ( sgWriteError() ) {
794         cout << "We detected an error while writing the file." << endl;
795         return false;
796     }
797
798     return true;
799 }
800
801
802 // write out the structures to an ASCII file.  We assume that the
803 // groups come to us sorted by material property.  If not, things
804 // don't break, but the result won't be as optimal.
805 bool sgWriteAsciiObj( const string& base, const string& name, const SGBucket& b,
806                       SGBinObject *obj )
807 {
808     Point3D p;
809     int i, j;
810
811     Point3D gbs_center = obj->get_gbs_center();
812     float gbs_radius = obj->get_gbs_radius();
813     point_list wgs84_nodes = obj->get_wgs84_nodes();
814     point_list normals = obj->get_normals();
815     point_list texcoords = obj->get_texcoords();
816     group_list tris_v = obj->get_tris_v();
817     group_list tris_tc = obj->get_tris_tc(); 
818     string_list tri_materials = obj->get_tri_materials();
819     group_list strips_v = obj->get_strips_v();
820     group_list strips_tc = obj->get_strips_tc(); 
821     string_list strip_materials = obj->get_strip_materials();
822     group_list fans_v = obj->get_fans_v();
823     group_list fans_tc = obj->get_fans_tc();
824     string_list fan_materials = obj->get_fan_materials();
825
826     string dir = base + "/" + b.gen_base_path();
827     string command = "mkdir -p " + dir;
828 #ifdef _MSC_VER
829     fg_mkdir( dir.c_str() );
830 #else
831     system(command.c_str());
832 #endif
833
834     // string file = dir + "/" + b.gen_index_str();
835     string file = dir + "/" + name;
836     cout << "Output file = " << file << endl;
837
838     FILE *fp;
839     if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
840         cout << "ERROR: opening " << file << " for writing!" << endl;
841         return false;
842     }
843
844     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
845          << tri_materials.size() << endl;
846     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
847          << strip_materials.size() << endl;
848     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
849          << fan_materials.size() << endl;
850
851     cout << "points = " << wgs84_nodes.size() << endl;
852     cout << "tex coords = " << texcoords.size() << endl;
853     // write headers
854     fprintf(fp, "# FGFS Scenery\n");
855     fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
856
857     time_t calendar_time = time(NULL);
858     struct tm *local_tm;
859     local_tm = localtime( &calendar_time );
860     char time_str[256];
861     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
862     fprintf(fp, "# Created %s\n", time_str );
863     fprintf(fp, "\n");
864
865     // write bounding sphere
866     fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
867             gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
868     fprintf(fp, "\n");
869
870     // dump vertex list
871     fprintf(fp, "# vertex list\n");
872     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
873         p = wgs84_nodes[i] - gbs_center;
874         
875         fprintf(fp,  "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
876     }
877     fprintf(fp, "\n");
878
879     fprintf(fp, "# vertex normal list\n");
880     for ( i = 0; i < (int)normals.size(); ++i ) {
881         p = normals[i];
882         fprintf(fp,  "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
883     }
884     fprintf(fp, "\n");
885
886     // dump texture coordinates
887     fprintf(fp, "# texture coordinate list\n");
888     for ( i = 0; i < (int)texcoords.size(); ++i ) {
889         p = texcoords[i];
890         fprintf(fp,  "vt %.5f %.5f\n", p.x(), p.y() );
891     }
892     fprintf(fp, "\n");
893
894     // dump individual triangles if they exist
895     if ( tris_v.size() > 0 ) {
896         fprintf(fp, "# triangle groups\n");
897
898         int start = 0;
899         int end = 1;
900         string material;
901         while ( start < (int)tri_materials.size() ) {
902             // find next group
903             material = tri_materials[start];
904             while ( (end < (int)tri_materials.size()) && 
905                     (material == tri_materials[end]) )
906             {
907                 // cout << "end = " << end << endl;
908                 end++;
909             }
910             // cout << "group = " << start << " to " << end - 1 << endl;
911
912             // make a list of points for the group
913             point_list group_nodes;
914             group_nodes.clear();
915             Point3D bs_center;
916             double bs_radius = 0;
917             for ( i = start; i < end; ++i ) {
918                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
919                     group_nodes.push_back( wgs84_nodes[ tris_v[i][j] ] );
920                     bs_center = calc_center( group_nodes );
921                     bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
922                 }
923             }
924
925             // write group headers
926             fprintf(fp, "\n");
927             fprintf(fp, "# usemtl %s\n", material.c_str());
928             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
929                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
930
931             // write groups
932             for ( i = start; i < end; ++i ) {
933                 fprintf(fp, "f");
934                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
935                     fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
936                 }
937                 fprintf(fp, "\n");
938             }
939
940             start = end;
941             end = start + 1;
942         }
943     }
944
945     // dump triangle groups
946     if ( strips_v.size() > 0 ) {
947         fprintf(fp, "# triangle strips\n");
948
949         int start = 0;
950         int end = 1;
951         string material;
952         while ( start < (int)strip_materials.size() ) {
953             // find next group
954             material = strip_materials[start];
955             while ( (end < (int)strip_materials.size()) && 
956                     (material == strip_materials[end]) )
957                 {
958                     // cout << "end = " << end << endl;
959                     end++;
960                 }
961             // cout << "group = " << start << " to " << end - 1 << endl;
962
963             // make a list of points for the group
964             point_list group_nodes;
965             group_nodes.clear();
966             Point3D bs_center;
967             double bs_radius = 0;
968             for ( i = start; i < end; ++i ) {
969                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
970                     group_nodes.push_back( wgs84_nodes[ strips_v[i][j] ] );
971                     bs_center = calc_center( group_nodes );
972                     bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
973                 }
974             }
975
976             // write group headers
977             fprintf(fp, "\n");
978             fprintf(fp, "# usemtl %s\n", material.c_str());
979             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
980                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
981
982             // write groups
983             for ( i = start; i < end; ++i ) {
984                 fprintf(fp, "ts");
985                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
986                     fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
987                 }
988                 fprintf(fp, "\n");
989             }
990             
991             start = end;
992             end = start + 1;
993         }
994     }
995
996     // close the file
997     fclose(fp);
998
999     command = "gzip --force --best " + file;
1000     system(command.c_str());
1001
1002     return true;
1003 }