]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_binobj.cxx
From Benoit Laniel: replace SG threading constructs with those from OpenThreads
[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  - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22 //
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <simgear_config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30 #include <simgear/debug/logstream.hxx>
31
32 #include <stdio.h>
33 #include <time.h>
34
35 #include <vector>
36 #include STL_STRING
37 #include <iostream>
38
39 #include <simgear/bucket/newbucket.hxx>
40 #include <simgear/misc/sg_path.hxx>
41
42 #include "lowlevel.hxx"
43 #include "sg_binobj.hxx"
44
45
46 SG_USING_STD( string );
47 SG_USING_STD( vector );
48
49 using std::cout;
50 using std::endl;
51
52 enum sgObjectTypes {
53     SG_BOUNDING_SPHERE = 0,
54
55     SG_VERTEX_LIST = 1,
56     SG_COLOR_LIST = 4,
57     SG_NORMAL_LIST = 2,
58     SG_TEXCOORD_LIST = 3,
59
60     SG_POINTS = 9,
61
62     SG_TRIANGLE_FACES = 10,
63     SG_TRIANGLE_STRIPS = 11,
64     SG_TRIANGLE_FANS = 12
65 };
66
67 enum sgIndexTypes {
68     SG_IDX_VERTICES =  0x01,
69     SG_IDX_NORMALS =   0x02,
70     SG_IDX_COLORS =    0x04,
71     SG_IDX_TEXCOORDS = 0x08
72 };
73
74 enum sgPropertyTypes {
75     SG_MATERIAL = 0,
76     SG_INDEX_TYPES = 1
77 };
78
79
80 class sgSimpleBuffer {
81
82 private:
83
84     char *ptr;
85     unsigned int size;
86
87 public:
88
89     inline sgSimpleBuffer( unsigned int s )
90     {
91         size = 1;
92         while ( size < s ) {
93             size *= 2;
94         }
95         SG_LOG(SG_EVENT, SG_DEBUG, "Creating a new buffer of size = " << size);
96         ptr = new char[size];
97     }
98
99     inline ~sgSimpleBuffer() {
100         delete [] ptr;
101     }
102
103     inline unsigned int get_size() const { return size; }
104     inline char *get_ptr() const { return ptr; }
105     inline void resize( unsigned int s ) {
106         if ( s > size ) {
107             if ( ptr != NULL ) {
108                 delete [] ptr;
109             }
110             while ( size < s ) {
111                 size *= 2;
112             }
113             SG_LOG(SG_EVENT, SG_DEBUG, "resizing buffer to size = " << size);
114             ptr = new char[size];
115         }
116     }
117 };
118
119
120 // calculate the center of a list of points, by taking the halfway
121 // point between the min and max points.
122 Point3D sgCalcCenter( point_list& wgs84_nodes ) {
123     Point3D p, min, max;
124
125     if ( wgs84_nodes.size() ) {
126         min = max = wgs84_nodes[0];
127     } else {
128         min = max = Point3D( 0 );
129     }
130
131     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
132         p = wgs84_nodes[i];
133
134         if ( p.x() < min.x() ) { min.setx( p.x() ); }
135         if ( p.y() < min.y() ) { min.sety( p.y() ); }
136         if ( p.z() < min.z() ) { min.setz( p.z() ); }
137
138         if ( p.x() > max.x() ) { max.setx( p.x() ); }
139         if ( p.y() > max.y() ) { max.sety( p.y() ); }
140         if ( p.z() > max.z() ) { max.setz( p.z() ); }
141     }
142
143     return ( min + max ) / 2.0;
144 }
145
146 // calculate the bounding sphere.  Center is the center of the
147 // tile and zero elevation
148 double sgCalcBoundingRadius( Point3D center, point_list& wgs84_nodes ) {
149     double dist_squared;
150     double radius_squared = 0;
151     
152     for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
153         dist_squared = center.distance3Dsquared( wgs84_nodes[i] );
154         if ( dist_squared > radius_squared ) {
155             radius_squared = dist_squared;
156         }
157     }
158
159     return sqrt(radius_squared);
160 }
161
162
163
164 // read object properties
165 static void read_object( gzFile fp,
166                          int obj_type,
167                          int nproperties,
168                          int nelements,
169                          group_list *vertices,
170                          group_list *normals,
171                          group_list *colors,
172                          group_list *texcoords,
173                          string_list *materials )
174 {
175     unsigned int nbytes;
176     unsigned char idx_mask;
177     int idx_size;
178     bool do_vertices, do_normals, do_colors, do_texcoords;
179     int j, k, idx;
180     sgSimpleBuffer buf( 32768 );  // 32 Kb
181     char material[256];
182
183     // default values
184     if ( obj_type == SG_POINTS ) {
185         idx_size = 1;
186         idx_mask = SG_IDX_VERTICES;
187         do_vertices = true;
188         do_normals = false;
189         do_colors = false;
190         do_texcoords = false;
191     } else {
192         idx_size = 2;
193         idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS);
194         do_vertices = true;
195         do_normals = false;
196         do_colors = false;
197         do_texcoords = true;
198     }
199
200     for ( j = 0; j < nproperties; ++j ) {
201         char prop_type;
202         sgReadChar( fp, &prop_type );
203
204         sgReadUInt( fp, &nbytes );
205         // cout << "property size = " << nbytes << endl;
206         if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
207         char *ptr = buf.get_ptr();
208         sgReadBytes( fp, nbytes, ptr );
209         if ( prop_type == SG_MATERIAL ) {
210             strncpy( material, ptr, nbytes );
211             material[nbytes] = '\0';
212             // cout << "material type = " << material << endl;
213         } else if ( prop_type == SG_INDEX_TYPES ) {
214             idx_mask = ptr[0];
215             // cout << "idx_mask = " << (int)idx_mask << endl;
216             idx_size = 0;
217             do_vertices = false;
218             do_normals = false;
219             do_colors = false;
220             do_texcoords = false;
221             if ( idx_mask & SG_IDX_VERTICES ) {
222                 do_vertices = true;
223                 ++idx_size;
224             }
225             if ( idx_mask & SG_IDX_NORMALS ) {
226                 do_normals = true;
227                 ++idx_size;
228             }
229             if ( idx_mask & SG_IDX_COLORS ) {
230                 do_colors = true;
231                 ++idx_size;
232             }
233             if ( idx_mask & SG_IDX_TEXCOORDS ) {
234                 do_texcoords = true;
235                 ++idx_size;
236             }
237         }
238     }
239
240     for ( j = 0; j < nelements; ++j ) {
241         sgReadUInt( fp, &nbytes );
242         // cout << "element size = " << nbytes << endl;
243         if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
244         char *ptr = buf.get_ptr();
245         sgReadBytes( fp, nbytes, ptr );
246         int count = nbytes / (idx_size * sizeof(unsigned short));
247         unsigned short *sptr = (unsigned short *)ptr;
248         int_list vs; vs.clear();
249         int_list ns; ns.clear();
250         int_list cs; cs.clear();
251         int_list tcs; tcs.clear();
252         for ( k = 0; k < count; ++k ) {
253             if ( sgIsBigEndian() ) {
254                 for ( idx = 0; idx < idx_size; ++idx ) {
255                     sgEndianSwap( (uint16_t *)&(sptr[idx]) );
256                 }
257             }
258             idx = 0;
259             if ( do_vertices ) {
260                 vs.push_back( sptr[idx++] );
261             }
262             if ( do_normals ) {
263                 ns.push_back( sptr[idx++] );
264                     }
265             if ( do_colors ) {
266                 cs.push_back( sptr[idx++] );
267             }
268             if ( do_texcoords ) {
269                 tcs.push_back( sptr[idx++] );
270             }
271             // cout << sptr[0] << " ";
272             sptr += idx_size;
273         }
274         // cout << endl;
275         vertices->push_back( vs );
276         normals->push_back( ns );
277         colors->push_back( cs );
278         texcoords->push_back( tcs );
279         materials->push_back( material );
280     }
281 }
282
283
284 // read a binary file and populate the provided structures.
285 bool SGBinObject::read_bin( const string& file ) {
286     SGVec3d p;
287     int i, j, k;
288     unsigned int nbytes;
289     sgSimpleBuffer buf( 32768 );  // 32 Kb
290
291     // zero out structures
292     gbs_center = SGVec3d(0, 0, 0);
293     gbs_radius = 0.0;
294
295     wgs84_nodes.clear();
296     normals.clear();
297     texcoords.clear();
298
299     pts_v.clear();
300     pts_n.clear();
301     pts_c.clear();
302     pts_tc.clear();
303     pt_materials.clear();
304
305     tris_v.clear();
306     tris_n.clear();
307     tris_c.clear();
308     tris_tc.clear();
309     tri_materials.clear();
310
311     strips_v.clear();
312     strips_n.clear();
313     strips_c.clear();
314     strips_tc.clear();
315     strip_materials.clear();
316
317     fans_v.clear();
318     fans_n.clear();
319     fans_c.clear();
320     fans_tc.clear();
321     fan_materials.clear();
322
323     gzFile fp;
324     if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
325         string filegz = file + ".gz";
326         if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
327             SG_LOG( SG_EVENT, SG_ALERT,
328                "ERROR: opening " << file << " or " << filegz << " for reading!");
329
330             return false;
331         }
332     }
333
334     sgClearReadError();
335
336     // read headers
337     unsigned int header;
338     sgReadUInt( fp, &header );
339     if ( ((header & 0xFF000000) >> 24) == 'S' &&
340          ((header & 0x00FF0000) >> 16) == 'G' ) {
341         // cout << "Good header" << endl;
342         // read file version
343         version = (header & 0x0000FFFF);
344         // cout << "File version = " << version << endl;
345     } else {
346         // close the file before we return
347         gzclose(fp);
348
349         return false;
350     }
351
352     // read creation time
353     unsigned int foo_calendar_time;
354     sgReadUInt( fp, &foo_calendar_time );
355
356 #if 0
357     time_t calendar_time = foo_calendar_time;
358     // The following code has a global effect on the host application
359     // and can screws up the time elsewhere.  It should be avoided
360     // unless you need this for debugging in which case you should
361     // disable it again once the debugging task is finished.
362     struct tm *local_tm;
363     local_tm = localtime( &calendar_time );
364     char time_str[256];
365     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
366     SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str);
367 #endif
368
369     // read number of top level objects
370     unsigned short nobjects;
371     if ( version >= 7 ) {
372         sgReadUShort( fp, &nobjects );
373     } else {
374         short tmp;
375         sgReadShort( fp, &tmp );
376         nobjects = tmp;
377     }
378     // cout << "Total objects to read = " << nobjects << endl;
379
380     // read in objects
381     for ( i = 0; i < nobjects; ++i ) {
382         // read object header
383         char obj_type;
384         unsigned short nproperties, nelements;
385         sgReadChar( fp, &obj_type );
386         if ( version >= 7 ) {
387             sgReadUShort( fp, &nproperties );
388             sgReadUShort( fp, &nelements );
389         } else {
390             short tmp;
391             sgReadShort( fp, &tmp );
392             nproperties = tmp;
393             sgReadShort( fp, &tmp );
394             nelements = tmp;
395         }
396
397         // cout << "object " << i << " = " << (int)obj_type << " props = "
398         //      << nproperties << " elements = " << nelements << endl;
399             
400         if ( obj_type == SG_BOUNDING_SPHERE ) {
401             // read bounding sphere properties
402             for ( j = 0; j < nproperties; ++j ) {
403                 char prop_type;
404                 sgReadChar( fp, &prop_type );
405
406                 sgReadUInt( fp, &nbytes );
407                 // cout << "property size = " << nbytes << endl;
408                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
409                 char *ptr = buf.get_ptr();
410                 sgReadBytes( fp, nbytes, ptr );
411             }
412
413             // read bounding sphere elements
414             for ( j = 0; j < nelements; ++j ) {
415                 sgReadUInt( fp, &nbytes );
416                 // cout << "element size = " << nbytes << endl;
417                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
418                 char *ptr = buf.get_ptr();
419                 sgReadBytes( fp, nbytes, ptr );
420
421                 double *dptr = (double *)ptr;
422                 if ( sgIsBigEndian() ) {
423                     sgEndianSwap( (uint64_t *)&(dptr[0]) );
424                     sgEndianSwap( (uint64_t *)&(dptr[1]) );
425                     sgEndianSwap( (uint64_t *)&(dptr[2]) );
426                 }
427                 gbs_center = SGVec3d( dptr[0], dptr[1], dptr[2] );
428                 // cout << "Center = " << gbs_center << endl;
429                 ptr += sizeof(double) * 3;
430                 
431                 float *fptr = (float *)ptr;
432                 if ( sgIsBigEndian() ) {
433                     sgEndianSwap( (uint32_t *)fptr );
434                 }
435                 gbs_radius = fptr[0];
436                 // cout << "Bounding radius = " << gbs_radius << endl;
437             }
438         } else if ( obj_type == SG_VERTEX_LIST ) {
439             // read vertex list properties
440             for ( j = 0; j < nproperties; ++j ) {
441                 char prop_type;
442                 sgReadChar( fp, &prop_type );
443
444                 sgReadUInt( fp, &nbytes );
445                 // cout << "property size = " << nbytes << endl;
446                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
447                 char *ptr = buf.get_ptr();
448                 sgReadBytes( fp, nbytes, ptr );
449             }
450
451             // read vertex list elements
452             for ( j = 0; j < nelements; ++j ) {
453                 sgReadUInt( fp, &nbytes );
454                 // cout << "element size = " << nbytes << endl;
455                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
456                 char *ptr = buf.get_ptr();
457                 sgReadBytes( fp, nbytes, ptr );
458                 int count = nbytes / (sizeof(float) * 3);
459                 float *fptr = (float *)ptr;
460                 wgs84_nodes.reserve( count );
461                 for ( k = 0; k < count; ++k ) {
462                     if ( sgIsBigEndian() ) {
463                         sgEndianSwap( (uint32_t *)&(fptr[0]) );
464                         sgEndianSwap( (uint32_t *)&(fptr[1]) );
465                         sgEndianSwap( (uint32_t *)&(fptr[2]) );
466                     }
467                     wgs84_nodes.push_back( SGVec3d(fptr[0], fptr[1], fptr[2]) );
468                     fptr += 3;
469                 }
470             }
471         } else if ( obj_type == SG_COLOR_LIST ) {
472             // read color list properties
473             for ( j = 0; j < nproperties; ++j ) {
474                 char prop_type;
475                 sgReadChar( fp, &prop_type );
476
477                 sgReadUInt( fp, &nbytes );
478                 // cout << "property size = " << nbytes << endl;
479                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
480                 char *ptr = buf.get_ptr();
481                 sgReadBytes( fp, nbytes, ptr );
482             }
483
484             // read color list elements
485             for ( j = 0; j < nelements; ++j ) {
486                 sgReadUInt( fp, &nbytes );
487                 // cout << "element size = " << nbytes << endl;
488                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
489                 char *ptr = buf.get_ptr();
490                 sgReadBytes( fp, nbytes, ptr );
491                 int count = nbytes / (sizeof(float) * 4);
492                 float *fptr = (float *)ptr;
493                 colors.reserve(count);
494                 for ( k = 0; k < count; ++k ) {
495                     if ( sgIsBigEndian() ) {
496                         sgEndianSwap( (uint32_t *)&(fptr[0]) );
497                         sgEndianSwap( (uint32_t *)&(fptr[1]) );
498                         sgEndianSwap( (uint32_t *)&(fptr[2]) );
499                         sgEndianSwap( (uint32_t *)&(fptr[3]) );
500                     }
501                     SGVec4f color( fptr[0], fptr[1], fptr[2], fptr[3] );
502                     colors.push_back( color );
503                     fptr += 4;
504                 }
505             }
506         } else if ( obj_type == SG_NORMAL_LIST ) {
507             // read normal list properties
508             for ( j = 0; j < nproperties; ++j ) {
509                 char prop_type;
510                 sgReadChar( fp, &prop_type );
511
512                 sgReadUInt( fp, &nbytes );
513                 // cout << "property size = " << nbytes << endl;
514                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
515                 char *ptr = buf.get_ptr();
516                 sgReadBytes( fp, nbytes, ptr );
517             }
518
519             // read normal list elements
520             for ( j = 0; j < nelements; ++j ) {
521                 sgReadUInt( fp, &nbytes );
522                 // cout << "element size = " << nbytes << endl;
523                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
524                 unsigned char *ptr = (unsigned char *)(buf.get_ptr());
525                 sgReadBytes( fp, nbytes, ptr );
526                 int count = nbytes / 3;
527                 normals.reserve( count );
528                 for ( k = 0; k < count; ++k ) {
529                     SGVec3f normal((ptr[0]) / 127.5 - 1.0,
530                                    (ptr[1]) / 127.5 - 1.0,
531                                    (ptr[2]) / 127.5 - 1.0);
532
533                     normals.push_back(normalize(normal));
534                     ptr += 3;
535                 }
536             }
537         } else if ( obj_type == SG_TEXCOORD_LIST ) {
538             // read texcoord list properties
539             for ( j = 0; j < nproperties; ++j ) {
540                 char prop_type;
541                 sgReadChar( fp, &prop_type );
542
543                 sgReadUInt( fp, &nbytes );
544                 // cout << "property size = " << nbytes << endl;
545                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
546                 char *ptr = buf.get_ptr();
547                 sgReadBytes( fp, nbytes, ptr );
548             }
549
550             // read texcoord list elements
551             for ( j = 0; j < nelements; ++j ) {
552                 sgReadUInt( fp, &nbytes );
553                 // cout << "element size = " << nbytes << endl;
554                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
555                 char *ptr = buf.get_ptr();
556                 sgReadBytes( fp, nbytes, ptr );
557                 int count = nbytes / (sizeof(float) * 2);
558                 float *fptr = (float *)ptr;
559                 texcoords.reserve(count);
560                 for ( k = 0; k < count; ++k ) {
561                     if ( sgIsBigEndian() ) {
562                         sgEndianSwap( (uint32_t *)&(fptr[0]) );
563                         sgEndianSwap( (uint32_t *)&(fptr[1]) );
564                     }
565                     texcoords.push_back( SGVec2f( fptr[0], fptr[1] ) );
566                     fptr += 2;
567                 }
568             }
569         } else if ( obj_type == SG_POINTS ) {
570             // read point elements
571             read_object( fp, SG_POINTS, nproperties, nelements,
572                          &pts_v, &pts_n, &pts_c, &pts_tc, &pt_materials );
573         } else if ( obj_type == SG_TRIANGLE_FACES ) {
574             // read triangle face properties
575             read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
576                          &tris_v, &tris_n, &tris_c, &tris_tc, &tri_materials );
577         } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
578             // read triangle strip properties
579             read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
580                          &strips_v, &strips_n, &strips_c, &strips_tc,
581                          &strip_materials );
582         } else if ( obj_type == SG_TRIANGLE_FANS ) {
583             // read triangle fan properties
584             read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
585                          &fans_v, &fans_n, &fans_c, &fans_tc, &fan_materials );
586         } else {
587             // unknown object type, just skip
588
589             // read properties
590             for ( j = 0; j < nproperties; ++j ) {
591                 char prop_type;
592                 sgReadChar( fp, &prop_type );
593
594                 sgReadUInt( fp, &nbytes );
595                 // cout << "property size = " << nbytes << endl;
596                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
597                 char *ptr = buf.get_ptr();
598                 sgReadBytes( fp, nbytes, ptr );
599             }
600
601             // read elements
602             for ( j = 0; j < nelements; ++j ) {
603                 sgReadUInt( fp, &nbytes );
604                 // cout << "element size = " << nbytes << endl;
605                 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
606                 char *ptr = buf.get_ptr();
607                 sgReadBytes( fp, nbytes, ptr );
608             }
609         }
610     }
611
612     // close the file
613     gzclose(fp);
614
615     if ( sgReadError() ) {
616         cout << "We detected an error while reading the file." << endl;
617         return false;
618     }
619
620     return true;
621 }
622
623
624 // write out the structures to a binary file.  We assume that the
625 // groups come to us sorted by material property.  If not, things
626 // don't break, but the result won't be as optimal.
627 bool SGBinObject::write_bin( const string& base, const string& name,
628                              const SGBucket& b )
629 {
630     int i, j;
631     unsigned char idx_mask;
632     int idx_size;
633
634     SGPath file = base + "/" + b.gen_base_path() + "/" + name + ".gz";
635     file.create_dir( 0755 );
636     cout << "Output file = " << file.str() << endl;
637
638     gzFile fp;
639     if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
640         cout << "ERROR: opening " << file.str() << " for writing!" << endl;
641         return false;
642     }
643
644     sgClearWriteError();
645
646     cout << "points size = " << pts_v.size() << "  pt_materials = " 
647          << pt_materials.size() << endl;
648     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
649          << tri_materials.size() << endl;
650     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
651          << strip_materials.size() << endl;
652     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
653          << fan_materials.size() << endl;
654
655     cout << "nodes = " << wgs84_nodes.size() << endl;
656     cout << "colors = " << colors.size() << endl;
657     cout << "normals = " << normals.size() << endl;
658     cout << "tex coords = " << texcoords.size() << endl;
659
660     // write header magic
661     sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
662     time_t calendar_time = time(NULL);
663     sgWriteLong( fp, (int32_t)calendar_time );
664
665     // calculate and write number of top level objects
666     string material;
667     int start;
668     int end;
669     unsigned short nobjects = 0;
670     nobjects++;                 // for gbs
671     nobjects++;                 // for vertices
672     nobjects++;                 // for colors
673     nobjects++;                 // for normals
674     nobjects++;                 // for texcoords
675
676     // points
677     unsigned short npts = 0;
678     start = 0; end = 1;
679     while ( start < (int)pt_materials.size() ) {
680         material = pt_materials[start];
681         while ( (end < (int)pt_materials.size()) &&
682                 (material == pt_materials[end]) ) {
683             end++;
684         }
685         npts++;
686         start = end; end = start + 1;
687     }
688     nobjects += npts;
689
690     // tris
691     unsigned short ntris = 0;
692     start = 0; end = 1;
693     while ( start < (int)tri_materials.size() ) {
694         material = tri_materials[start];
695         while ( (end < (int)tri_materials.size()) &&
696                 (material == tri_materials[end]) ) {
697             end++;
698         }
699         ntris++;
700         start = end; end = start + 1;
701     }
702     nobjects += ntris;
703
704     // strips
705     unsigned short nstrips = 0;
706     start = 0; end = 1;
707     while ( start < (int)strip_materials.size() ) {
708         material = strip_materials[start];
709         while ( (end < (int)strip_materials.size()) &&
710                 (material == strip_materials[end]) ) {
711             end++;
712         }
713         nstrips++;
714         start = end; end = start + 1;
715     }
716     nobjects += nstrips;
717
718     // fans
719     unsigned short nfans = 0;
720     start = 0; end = 1;
721     while ( start < (int)fan_materials.size() ) {
722         material = fan_materials[start];
723         while ( (end < (int)fan_materials.size()) &&
724                 (material == fan_materials[end]) ) {
725             end++;
726         }
727         nfans++;
728         start = end; end = start + 1;
729     }
730     nobjects += nfans;
731
732     cout << "total top level objects = " << nobjects << endl;
733     sgWriteUShort( fp, nobjects );
734
735     // write bounding sphere
736     sgWriteChar( fp, (char)SG_BOUNDING_SPHERE );        // type
737     sgWriteUShort( fp, 0 );                             // nproperties
738     sgWriteUShort( fp, 1 );                             // nelements
739
740     sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
741     sgdVec3 center;
742     sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
743     sgWritedVec3( fp, center );
744     sgWriteFloat( fp, gbs_radius );
745
746     // dump vertex list
747     sgWriteChar( fp, (char)SG_VERTEX_LIST );             // type
748     sgWriteUShort( fp, 0 );                              // nproperties
749     sgWriteUShort( fp, 1 );                              // nelements
750     sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
751     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
752         SGVec3f p = toVec3f(wgs84_nodes[i] - gbs_center);
753         sgWriteVec3( fp, p.data() );
754     }
755
756     // dump vertex color list
757     sgWriteChar( fp, (char)SG_COLOR_LIST );             // type
758     sgWriteUShort( fp, 0 );                              // nproperties
759     sgWriteUShort( fp, 1 );                              // nelements
760     sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes
761     for ( i = 0; i < (int)colors.size(); ++i ) {
762         sgWriteVec4( fp, colors[i].data() );
763     }
764
765     // dump vertex normal list
766     sgWriteChar( fp, (char)SG_NORMAL_LIST );            // type
767     sgWriteUShort( fp, 0 );                             // nproperties
768     sgWriteUShort( fp, 1 );                             // nelements
769     sgWriteUInt( fp, normals.size() * 3 );              // nbytes
770     char normal[3];
771     for ( i = 0; i < (int)normals.size(); ++i ) {
772         SGVec3f p = normals[i];
773         normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
774         normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
775         normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
776         sgWriteBytes( fp, 3, normal );
777     }
778
779     // dump texture coordinates
780     sgWriteChar( fp, (char)SG_TEXCOORD_LIST );          // type
781     sgWriteUShort( fp, 0 );                             // nproperties
782     sgWriteUShort( fp, 1 );                             // nelements
783     sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
784     for ( i = 0; i < (int)texcoords.size(); ++i ) {
785         sgWriteVec2( fp, texcoords[i].data() );
786     }
787
788     // dump point groups if they exist
789     if ( pts_v.size() > 0 ) {
790         int start = 0;
791         int end = 1;
792         string material;
793         while ( start < (int)pt_materials.size() ) {
794             // find next group
795             material = pt_materials[start];
796             while ( (end < (int)pt_materials.size()) && 
797                     (material == pt_materials[end]) )
798                 {
799                     // cout << "end = " << end << endl;
800                     end++;
801                 }
802             // cout << "group = " << start << " to " << end - 1 << endl;
803
804             // write group headers
805             sgWriteChar( fp, (char)SG_POINTS );          // type
806             sgWriteUShort( fp, 2 );                      // nproperties
807             sgWriteUShort( fp, end - start );             // nelements
808
809             sgWriteChar( fp, (char)SG_MATERIAL );        // property
810             sgWriteUInt( fp, material.length() );        // nbytes
811             sgWriteBytes( fp, material.length(), material.c_str() );
812
813             idx_mask = 0;
814             idx_size = 0;
815             if ( pts_v.size() ) { idx_mask |= SG_IDX_VERTICES; ++idx_size; }
816             if ( pts_n.size() ) { idx_mask |= SG_IDX_NORMALS; ++idx_size; }
817             if ( pts_c.size() ) { idx_mask |= SG_IDX_COLORS; ++idx_size; }
818             if ( pts_tc.size() ) { idx_mask |= SG_IDX_TEXCOORDS; ++idx_size; }
819             sgWriteChar( fp, (char)SG_INDEX_TYPES );     // property
820             sgWriteUInt( fp, 1 );                        // nbytes
821             sgWriteChar( fp, idx_mask );
822
823             // write strips
824             for ( i = start; i < end; ++i ) {
825                 // nbytes
826                 sgWriteUInt( fp, pts_v[i].size() * idx_size
827                                  * sizeof(unsigned short) );
828                 for ( j = 0; j < (int)pts_v[i].size(); ++j ) {
829                     if ( pts_v.size() ) { 
830                         sgWriteUShort( fp, (unsigned short)pts_v[i][j] );
831                     }
832                     if ( pts_n.size() ) { 
833                         sgWriteUShort( fp, (unsigned short)pts_n[i][j] );
834                     }
835                     if ( pts_c.size() ) { 
836                         sgWriteUShort( fp, (unsigned short)pts_c[i][j] );
837                     }
838                     if ( pts_tc.size() ) { 
839                         sgWriteUShort( fp, (unsigned short)pts_tc[i][j] );
840                     }
841                 }
842             }
843             
844             start = end;
845             end = start + 1;
846         }
847     }
848
849     // dump individual triangles if they exist
850     if ( tris_v.size() > 0 ) {
851         int start = 0;
852         int end = 1;
853         string material;
854         while ( start < (int)tri_materials.size() ) {
855             // find next group
856             material = tri_materials[start];
857             while ( (end < (int)tri_materials.size()) && 
858                     (material == tri_materials[end]) &&
859                     3*(end-start) < 32760 )
860             {
861                 // cout << "end = " << end << endl;
862                 end++;
863             }
864             // cout << "group = " << start << " to " << end - 1 << endl;
865
866             // write group headers
867             sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
868             sgWriteUShort( fp, 2 );                     // nproperties
869             sgWriteUShort( fp, 1 );                      // nelements
870
871             sgWriteChar( fp, (char)SG_MATERIAL );       // property
872             sgWriteUInt( fp, material.length() );        // nbytes
873             sgWriteBytes( fp, material.length(), material.c_str() );
874
875             idx_mask = 0;
876             idx_size = 0;
877             if ( tris_v.size() ) { idx_mask |= SG_IDX_VERTICES; ++idx_size; }
878             if ( tris_n.size() ) { idx_mask |= SG_IDX_NORMALS; ++idx_size; }
879             if ( tris_c.size() ) { idx_mask |= SG_IDX_COLORS; ++idx_size; }
880             if ( tris_tc.size() ) { idx_mask |= SG_IDX_TEXCOORDS; ++idx_size; }
881             sgWriteChar( fp, (char)SG_INDEX_TYPES );     // property
882             sgWriteUInt( fp, 1 );                        // nbytes
883             sgWriteChar( fp, idx_mask );
884
885             // nbytes
886             sgWriteUInt( fp, (end - start) * 3 * idx_size
887                              * sizeof(unsigned short) );
888
889             // write group
890             for ( i = start; i < end; ++i ) {
891                 for ( j = 0; j < 3; ++j ) {
892                     if ( tris_v.size() ) {
893                         sgWriteUShort( fp, (unsigned short)tris_v[i][j] );
894                     }
895                     if ( tris_n.size() ) {
896                         sgWriteUShort( fp, (unsigned short)tris_n[i][j] );
897                     }
898                     if ( tris_c.size() ) {
899                         sgWriteUShort( fp, (unsigned short)tris_c[i][j] );
900                     }
901                     if ( tris_tc.size() ) {
902                         sgWriteUShort( fp, (unsigned short)tris_tc[i][j] );
903                     }
904                 }
905             }
906
907             start = end;
908             end = start + 1;
909         }
910     }
911
912     // dump triangle strips
913     if ( strips_v.size() > 0 ) {
914         int start = 0;
915         int end = 1;
916         string material;
917         while ( start < (int)strip_materials.size() ) {
918             // find next group
919             material = strip_materials[start];
920             while ( (end < (int)strip_materials.size()) && 
921                     (material == strip_materials[end]) )
922                 {
923                     // cout << "end = " << end << endl;
924                     end++;
925                 }
926             // cout << "group = " << start << " to " << end - 1 << endl;
927
928             // write group headers
929             sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
930             sgWriteUShort( fp, 2 );                      // nproperties
931             sgWriteUShort( fp, end - start );             // nelements
932
933             sgWriteChar( fp, (char)SG_MATERIAL );        // property
934             sgWriteUInt( fp, material.length() );        // nbytes
935             sgWriteBytes( fp, material.length(), material.c_str() );
936
937             idx_mask = 0;
938             idx_size = 0;
939             if ( strips_v.size() ) { idx_mask |= SG_IDX_VERTICES; ++idx_size; }
940             if ( strips_n.size() ) { idx_mask |= SG_IDX_NORMALS; ++idx_size; }
941             if ( strips_c.size() ) { idx_mask |= SG_IDX_COLORS; ++idx_size; }
942             if ( strips_tc.size() ) { idx_mask |= SG_IDX_TEXCOORDS; ++idx_size;}
943             sgWriteChar( fp, (char)SG_INDEX_TYPES );     // property
944             sgWriteUInt( fp, 1 );                        // nbytes
945             sgWriteChar( fp, idx_mask );
946
947             // write strips
948             for ( i = start; i < end; ++i ) {
949                 // nbytes
950                 sgWriteUInt( fp, strips_v[i].size() * idx_size
951                                  * sizeof(unsigned short));
952                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
953                     if ( strips_v.size() ) { 
954                         sgWriteUShort( fp, (unsigned short)strips_v[i][j] );
955                     }
956                     if ( strips_n.size() ) { 
957                         sgWriteUShort( fp, (unsigned short)strips_n[i][j] );
958                     }
959                     if ( strips_c.size() ) { 
960                         sgWriteUShort( fp, (unsigned short)strips_c[i][j] );
961                     }
962                     if ( strips_tc.size() ) { 
963                         sgWriteUShort( fp, (unsigned short)strips_tc[i][j] );
964                     }
965                 }
966             }
967             
968             start = end;
969             end = start + 1;
970         }
971     }
972
973     // dump triangle fans
974     if ( fans_v.size() > 0 ) {
975         int start = 0;
976         int end = 1;
977         string material;
978         while ( start < (int)fan_materials.size() ) {
979             // find next group
980             material = fan_materials[start];
981             while ( (end < (int)fan_materials.size()) && 
982                     (material == fan_materials[end]) )
983                 {
984                     // cout << "end = " << end << endl;
985                     end++;
986                 }
987             // cout << "group = " << start << " to " << end - 1 << endl;
988
989             // write group headers
990             sgWriteChar( fp, (char)SG_TRIANGLE_FANS );   // type
991             sgWriteUShort( fp, 2 );                      // nproperties
992             sgWriteUShort( fp, end - start );             // nelements
993
994             sgWriteChar( fp, (char)SG_MATERIAL );       // property
995             sgWriteUInt( fp, material.length() );        // nbytes
996             sgWriteBytes( fp, material.length(), material.c_str() );
997
998             idx_mask = 0;
999             idx_size = 0;
1000             if ( fans_v.size() ) { idx_mask |= SG_IDX_VERTICES; ++idx_size; }
1001             if ( fans_n.size() ) { idx_mask |= SG_IDX_NORMALS; ++idx_size; }
1002             if ( fans_c.size() ) { idx_mask |= SG_IDX_COLORS; ++idx_size; }
1003             if ( fans_tc.size() ) { idx_mask |= SG_IDX_TEXCOORDS; ++idx_size; }
1004             sgWriteChar( fp, (char)SG_INDEX_TYPES );     // property
1005             sgWriteUInt( fp, 1 );                        // nbytes
1006             sgWriteChar( fp, idx_mask );
1007
1008             // write fans
1009             for ( i = start; i < end; ++i ) {
1010                 // nbytes
1011                 sgWriteUInt( fp, fans_v[i].size() * idx_size
1012                                  * sizeof(unsigned short) );
1013                 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
1014                     if ( fans_v.size() ) {
1015                         sgWriteUShort( fp, (unsigned short)fans_v[i][j] );
1016                     }
1017                     if ( fans_n.size() ) {
1018                         sgWriteUShort( fp, (unsigned short)fans_n[i][j] );
1019                     }
1020                     if ( fans_c.size() ) {
1021                         sgWriteUShort( fp, (unsigned short)fans_c[i][j] );
1022                     }
1023                     if ( fans_tc.size() ) {
1024                         sgWriteUShort( fp, (unsigned short)fans_tc[i][j] );
1025                     }
1026                 }
1027             }
1028             
1029             start = end;
1030             end = start + 1;
1031         }
1032     }
1033
1034     // close the file
1035     gzclose(fp);
1036
1037     if ( sgWriteError() ) {
1038         cout << "We detected an error while writing the file." << endl;
1039         return false;
1040     }
1041
1042     return true;
1043 }
1044
1045
1046 // write out the structures to an ASCII file.  We assume that the
1047 // groups come to us sorted by material property.  If not, things
1048 // don't break, but the result won't be as optimal.
1049 bool SGBinObject::write_ascii( const string& base, const string& name,
1050                                const SGBucket& b )
1051 {
1052     int i, j;
1053
1054     SGPath file = base + "/" + b.gen_base_path() + "/" + name;
1055     file.create_dir( 0755 );
1056     cout << "Output file = " << file.str() << endl;
1057
1058     FILE *fp;
1059     if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
1060         cout << "ERROR: opening " << file.str() << " for writing!" << endl;
1061         return false;
1062     }
1063
1064     cout << "triangles size = " << tris_v.size() << "  tri_materials = " 
1065          << tri_materials.size() << endl;
1066     cout << "strips size = " << strips_v.size() << "  strip_materials = " 
1067          << strip_materials.size() << endl;
1068     cout << "fans size = " << fans_v.size() << "  fan_materials = " 
1069          << fan_materials.size() << endl;
1070
1071     cout << "points = " << wgs84_nodes.size() << endl;
1072     cout << "tex coords = " << texcoords.size() << endl;
1073     // write headers
1074     fprintf(fp, "# FGFS Scenery\n");
1075     fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
1076
1077     time_t calendar_time = time(NULL);
1078     struct tm *local_tm;
1079     local_tm = localtime( &calendar_time );
1080     char time_str[256];
1081     strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
1082     fprintf(fp, "# Created %s\n", time_str );
1083     fprintf(fp, "\n");
1084
1085     // write bounding sphere
1086     fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
1087             gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
1088     fprintf(fp, "\n");
1089
1090     // dump vertex list
1091     fprintf(fp, "# vertex list\n");
1092     for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1093         SGVec3d p = wgs84_nodes[i] - gbs_center;
1094         
1095         fprintf(fp,  "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1096     }
1097     fprintf(fp, "\n");
1098
1099     fprintf(fp, "# vertex normal list\n");
1100     for ( i = 0; i < (int)normals.size(); ++i ) {
1101         SGVec3f p = normals[i];
1102         fprintf(fp,  "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1103     }
1104     fprintf(fp, "\n");
1105
1106     // dump texture coordinates
1107     fprintf(fp, "# texture coordinate list\n");
1108     for ( i = 0; i < (int)texcoords.size(); ++i ) {
1109         SGVec2f p = texcoords[i];
1110         fprintf(fp,  "vt %.5f %.5f\n", p.x(), p.y() );
1111     }
1112     fprintf(fp, "\n");
1113
1114     // dump individual triangles if they exist
1115     if ( tris_v.size() > 0 ) {
1116         fprintf(fp, "# triangle groups\n");
1117
1118         int start = 0;
1119         int end = 1;
1120         string material;
1121         while ( start < (int)tri_materials.size() ) {
1122             // find next group
1123             material = tri_materials[start];
1124             while ( (end < (int)tri_materials.size()) && 
1125                     (material == tri_materials[end]) )
1126             {
1127                 // cout << "end = " << end << endl;
1128                 end++;
1129             }
1130             // cout << "group = " << start << " to " << end - 1 << endl;
1131
1132             // make a list of points for the group
1133             point_list group_nodes;
1134             group_nodes.clear();
1135             SGVec3d bs_center;
1136             double bs_radius = 0;
1137             for ( i = start; i < end; ++i ) {
1138                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1139                     group_nodes.push_back( Point3D::fromSGVec3(wgs84_nodes[ tris_v[i][j] ]) );
1140                     bs_center = sgCalcCenter( group_nodes ).toSGVec3d();
1141                     bs_radius = sgCalcBoundingRadius( Point3D::fromSGVec3(bs_center), group_nodes );
1142                 }
1143             }
1144
1145             // write group headers
1146             fprintf(fp, "\n");
1147             fprintf(fp, "# usemtl %s\n", material.c_str());
1148             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1149                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1150
1151             // write groups
1152             for ( i = start; i < end; ++i ) {
1153                 fprintf(fp, "f");
1154                 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1155                     fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
1156                 }
1157                 fprintf(fp, "\n");
1158             }
1159
1160             start = end;
1161             end = start + 1;
1162         }
1163     }
1164
1165     // dump triangle groups
1166     if ( strips_v.size() > 0 ) {
1167         fprintf(fp, "# triangle strips\n");
1168
1169         int start = 0;
1170         int end = 1;
1171         string material;
1172         while ( start < (int)strip_materials.size() ) {
1173             // find next group
1174             material = strip_materials[start];
1175             while ( (end < (int)strip_materials.size()) && 
1176                     (material == strip_materials[end]) )
1177                 {
1178                     // cout << "end = " << end << endl;
1179                     end++;
1180                 }
1181             // cout << "group = " << start << " to " << end - 1 << endl;
1182
1183             // make a list of points for the group
1184             point_list group_nodes;
1185             group_nodes.clear();
1186             SGVec3d bs_center;
1187             double bs_radius = 0;
1188             for ( i = start; i < end; ++i ) {
1189                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1190                     group_nodes.push_back( Point3D::fromSGVec3(wgs84_nodes[ strips_v[i][j] ]) );
1191                     bs_center = sgCalcCenter( group_nodes ).toSGVec3d();
1192                     bs_radius = sgCalcBoundingRadius( Point3D::fromSGVec3(bs_center), group_nodes );
1193                 }
1194             }
1195
1196             // write group headers
1197             fprintf(fp, "\n");
1198             fprintf(fp, "# usemtl %s\n", material.c_str());
1199             fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1200                     bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1201
1202             // write groups
1203             for ( i = start; i < end; ++i ) {
1204                 fprintf(fp, "ts");
1205                 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1206                     fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
1207                 }
1208                 fprintf(fp, "\n");
1209             }
1210             
1211             start = end;
1212             end = start + 1;
1213         }
1214     }
1215
1216     // close the file
1217     fclose(fp);
1218
1219     string command = "gzip --force --best " + file.str();
1220     system(command.c_str());
1221
1222     return true;
1223 }