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