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