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