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