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