1 // sg_binobj.cxx -- routines to read and write low level flightgear 3d objects
3 // Written by Curtis Olson, started January 2000.
5 // Copyright (C) 2000 Curtis L. Olson - curt@flightgear.org
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <simgear/compiler.h>
38 #include <simgear/bucket/newbucket.hxx>
40 #include "lowlevel.hxx"
41 #include "sg_binobj.hxx"
44 # include <Win32/mkdir.hpp>
51 SG_BOUNDING_SPHERE = 0,
57 SG_TRIANGLE_FACES = 10,
58 SG_TRIANGLE_STRIPS = 11,
67 // calculate the center of a list of points, by taking the halfway
68 // point between the min and max points.
69 static Point3D calc_center( point_list& wgs84_nodes ) {
72 if ( wgs84_nodes.size() ) {
73 min = max = wgs84_nodes[0];
75 min = max = Point3D( 0 );
78 for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
81 if ( p.x() < min.x() ) { min.setx( p.x() ); }
82 if ( p.y() < min.y() ) { min.sety( p.y() ); }
83 if ( p.z() < min.z() ) { min.setz( p.z() ); }
85 if ( p.x() > max.x() ) { max.setx( p.x() ); }
86 if ( p.y() > max.y() ) { max.sety( p.y() ); }
87 if ( p.z() > max.z() ) { max.setz( p.z() ); }
90 return ( min + max ) / 2.0;
93 // calculate the bounding sphere. Center is the center of the
94 // tile and zero elevation
95 double sgCalcBoundingRadius( Point3D center, point_list& wgs84_nodes ) {
97 double radius_squared = 0;
99 for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) {
100 dist_squared = center.distance3Dsquared( wgs84_nodes[i] );
101 if ( dist_squared > radius_squared ) {
102 radius_squared = dist_squared;
106 return sqrt(radius_squared);
110 // read a binary file and populate the provided structures.
111 bool SGBinObject::read_bin( const string& file ) {
116 // zero out structures
117 gbs_center = Point3D( 0 );
126 tri_materials.clear();
130 strip_materials.clear();
134 fan_materials.clear();
136 cout << "Loading binary input file = " << file << endl;
139 if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
140 string filegz = file + ".gz";
141 if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
142 cout << "ERROR: opening " << file << " or " << filegz
143 << "for reading!" << endl;
152 sgReadInt( fp, &header );
153 if ( ((header & 0xFF000000) >> 24) == 'S' &&
154 ((header & 0x00FF0000) >> 16) == 'G' ) {
155 cout << "Good header" << endl;
157 version = (header & 0x0000FFFF);
158 cout << "File version = " << version << endl;
163 // read creation time
164 time_t calendar_time;
165 sgReadLong( fp, &calendar_time );
167 local_tm = localtime( &calendar_time );
169 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
170 cout << "File created on " << time_str << endl;
172 // read number of top level objects
174 sgReadShort( fp, &nobjects );
175 cout << "Total objects to read = " << nobjects << endl;
178 for ( i = 0; i < nobjects; ++i ) {
179 // read object header
181 short nproperties, nelements;
182 sgReadChar( fp, &obj_type );
183 sgReadShort( fp, &nproperties );
184 sgReadShort( fp, &nelements );
186 cout << "object " << i << " = " << (int)obj_type << " props = "
187 << nproperties << " elements = " << nelements << endl;
189 if ( obj_type == SG_BOUNDING_SPHERE ) {
190 // read bounding sphere properties
191 for ( j = 0; j < nproperties; ++j ) {
193 sgReadChar( fp, &prop_type );
196 sgReadInt( fp, &nbytes );
197 cout << "property size = " << nbytes << endl;
200 sgReadBytes( fp, nbytes, ptr );
203 // read bounding sphere elements
204 for ( j = 0; j < nelements; ++j ) {
206 sgReadInt( fp, &nbytes );
207 cout << "element size = " << nbytes << endl;
210 sgReadBytes( fp, nbytes, ptr );
212 double *dptr = (double *)ptr;
213 gbs_center = Point3D( dptr[0], dptr[1], dptr[2] );
214 cout << "Center = " << gbs_center << endl;
215 ptr += sizeof(double) * 3;
217 float *fptr = (float *)ptr;
218 gbs_radius = fptr[0];
219 cout << "Bounding radius = " << gbs_radius << endl;
221 } else if ( obj_type == SG_VERTEX_LIST ) {
222 // read vertex list properties
223 for ( j = 0; j < nproperties; ++j ) {
225 sgReadChar( fp, &prop_type );
228 sgReadInt( fp, &nbytes );
229 cout << "property size = " << nbytes << endl;
232 sgReadBytes( fp, nbytes, ptr );
235 // read vertex list elements
236 for ( j = 0; j < nelements; ++j ) {
238 sgReadInt( fp, &nbytes );
239 cout << "element size = " << nbytes << endl;
242 sgReadBytes( fp, nbytes, ptr );
243 int count = nbytes / (sizeof(float) * 3);
244 float *fptr = (float *)ptr;
245 for ( k = 0; k < count; ++k ) {
246 p = Point3D( fptr[0], fptr[1], fptr[2] );
247 cout << "node = " << p << endl;
248 wgs84_nodes.push_back( p );
252 } else if ( obj_type == SG_NORMAL_LIST ) {
253 // read normal list properties
254 for ( j = 0; j < nproperties; ++j ) {
256 sgReadChar( fp, &prop_type );
259 sgReadInt( fp, &nbytes );
260 cout << "property size = " << nbytes << endl;
263 sgReadBytes( fp, nbytes, ptr );
266 // read normal list elements
267 for ( j = 0; j < nelements; ++j ) {
269 sgReadInt( fp, &nbytes );
270 cout << "element size = " << nbytes << endl;
272 unsigned char *ptr = (unsigned char *)buf;
273 sgReadBytes( fp, nbytes, ptr );
274 int count = nbytes / 3;
275 for ( k = 0; k < count; ++k ) {
276 p = Point3D( ptr[0] / 128.0 - 1.0,
277 ptr[1] / 128.0 - 1.0,
278 ptr[2] / 128.0 - 1.0 );
279 cout << "normal = " << p << endl;
280 normals.push_back( p );
284 } else if ( obj_type == SG_TEXCOORD_LIST ) {
285 // read texcoord list properties
286 for ( j = 0; j < nproperties; ++j ) {
288 sgReadChar( fp, &prop_type );
291 sgReadInt( fp, &nbytes );
292 cout << "property size = " << nbytes << endl;
295 sgReadBytes( fp, nbytes, ptr );
298 // read texcoord list elements
299 for ( j = 0; j < nelements; ++j ) {
301 sgReadInt( fp, &nbytes );
302 cout << "element size = " << nbytes << endl;
305 sgReadBytes( fp, nbytes, ptr );
306 int count = nbytes / (sizeof(float) * 2);
307 float *fptr = (float *)ptr;
308 for ( k = 0; k < count; ++k ) {
309 p = Point3D( fptr[0], fptr[1], 0 );
310 cout << "texcoord = " << p << endl;
311 texcoords.push_back( p );
315 } else if ( obj_type == SG_TRIANGLE_FACES ) {
316 // read triangle face properties
317 for ( j = 0; j < nproperties; ++j ) {
319 sgReadChar( fp, &prop_type );
322 sgReadInt( fp, &nbytes );
323 cout << "property size = " << nbytes << endl;
326 sgReadBytes( fp, nbytes, ptr );
327 if ( prop_type == SG_MATERIAL ) {
328 strncpy( material, ptr, nbytes );
329 material[nbytes] = '\0';
330 cout << "material type = " << material << endl;
334 // read triangle face elements
335 for ( j = 0; j < nelements; ++j ) {
337 sgReadInt( fp, &nbytes );
338 cout << "element size = " << nbytes << endl;
341 sgReadBytes( fp, nbytes, ptr );
342 int count = nbytes / (sizeof(short) * 2);
343 short *sptr = (short *)ptr;
345 vs.clear(); tcs.clear();
346 for ( k = 0; k < count; ++k ) {
347 vs.push_back( sptr[0] );
348 tcs.push_back( sptr[1] );
349 cout << sptr[0] << "/" << sptr[1] << " ";
353 tris_v.push_back( vs );
354 tris_tc.push_back( tcs );
355 tri_materials.push_back( material );
357 } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
358 // read triangle strip properties
359 for ( j = 0; j < nproperties; ++j ) {
361 sgReadChar( fp, &prop_type );
364 sgReadInt( fp, &nbytes );
365 cout << "property size = " << nbytes << endl;
368 sgReadBytes( fp, nbytes, ptr );
369 if ( prop_type == SG_MATERIAL ) {
370 strncpy( material, ptr, nbytes );
371 material[nbytes] = '\0';
372 cout << "material type = " << material << endl;
376 // read triangle strip elements
377 for ( j = 0; j < nelements; ++j ) {
379 sgReadInt( fp, &nbytes );
380 cout << "element size = " << nbytes << endl;
383 sgReadBytes( fp, nbytes, ptr );
384 int count = nbytes / (sizeof(short) * 2);
385 short *sptr = (short *)ptr;
387 vs.clear(); tcs.clear();
388 for ( k = 0; k < count; ++k ) {
389 vs.push_back( sptr[0] );
390 tcs.push_back( sptr[1] );
391 cout << sptr[0] << "/" << sptr[1] << " ";
395 strips_v.push_back( vs );
396 strips_tc.push_back( tcs );
397 strip_materials.push_back( material );
399 } else if ( obj_type == SG_TRIANGLE_FANS ) {
400 // read triangle fan properties
401 for ( j = 0; j < nproperties; ++j ) {
403 sgReadChar( fp, &prop_type );
406 sgReadInt( fp, &nbytes );
407 cout << "property size = " << nbytes << endl;
410 sgReadBytes( fp, nbytes, ptr );
411 if ( prop_type == SG_MATERIAL ) {
412 strncpy( material, ptr, nbytes );
413 material[nbytes] = '\0';
414 cout << "material type = " << material << endl;
418 // read triangle fan elements
419 for ( j = 0; j < nelements; ++j ) {
421 sgReadInt( fp, &nbytes );
422 cout << "element size = " << nbytes << endl;
425 sgReadBytes( fp, nbytes, ptr );
426 int count = nbytes / (sizeof(short) * 2);
427 short *sptr = (short *)ptr;
429 vs.clear(); tcs.clear();
430 for ( k = 0; k < count; ++k ) {
431 vs.push_back( sptr[0] );
432 tcs.push_back( sptr[1] );
433 cout << sptr[0] << "/" << sptr[1] << " ";
437 fans_v.push_back( vs );
438 fans_tc.push_back( tcs );
439 fan_materials.push_back( material );
442 // unknown object type, just skip
445 for ( j = 0; j < nproperties; ++j ) {
447 sgReadChar( fp, &prop_type );
450 sgReadInt( fp, &nbytes );
451 cout << "property size = " << nbytes << endl;
454 sgReadBytes( fp, nbytes, ptr );
458 for ( j = 0; j < nelements; ++j ) {
460 sgReadInt( fp, &nbytes );
461 cout << "element size = " << nbytes << endl;
464 sgReadBytes( fp, nbytes, ptr );
472 if ( sgReadError() ) {
473 cout << "We detected an error while reading the file." << endl;
481 // write out the structures to a binary file. We assume that the
482 // groups come to us sorted by material property. If not, things
483 // don't break, but the result won't be as optimal.
484 bool SGBinObject::write_bin( const string& base, const string& name,
492 string dir = base + "/" + b.gen_base_path();
493 string command = "mkdir -p " + dir;
495 fg_mkdir( dir.c_str() );
497 system(command.c_str());
500 string file = dir + "/" + name + ".gz";
501 cout << "Output file = " << file << endl;
504 if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
505 cout << "ERROR: opening " << file << " for writing!" << endl;
511 cout << "triangles size = " << tris_v.size() << " tri_materials = "
512 << tri_materials.size() << endl;
513 cout << "strips size = " << strips_v.size() << " strip_materials = "
514 << strip_materials.size() << endl;
515 cout << "fans size = " << fans_v.size() << " fan_materials = "
516 << fan_materials.size() << endl;
518 cout << "points = " << wgs84_nodes.size() << endl;
519 cout << "tex coords = " << texcoords.size() << endl;
521 // write header magic
522 sgWriteInt( fp, SG_FILE_MAGIC_NUMBER );
523 time_t calendar_time = time(NULL);
524 sgWriteLong( fp, (long int)calendar_time );
526 // calculate and write number of top level objects
531 nobjects++; // for gbs
532 nobjects++; // for vertices
533 nobjects++; // for normals
534 nobjects++; // for texcoords
539 while ( start < (int)tri_materials.size() ) {
540 material = tri_materials[start];
541 while ( (end < (int)tri_materials.size()) &&
542 (material == tri_materials[end]) ) {
546 start = end; end = start + 1;
553 while ( start < (int)strip_materials.size() ) {
554 material = strip_materials[start];
555 while ( (end < (int)strip_materials.size()) &&
556 (material == strip_materials[end]) ) {
560 start = end; end = start + 1;
567 while ( start < (int)fan_materials.size() ) {
568 material = fan_materials[start];
569 while ( (end < (int)fan_materials.size()) &&
570 (material == fan_materials[end]) ) {
574 start = end; end = start + 1;
578 cout << "total top level objects = " << nobjects << endl;
579 sgWriteShort( fp, nobjects );
581 // write bounding sphere
582 sgWriteChar( fp, (char)SG_BOUNDING_SPHERE ); // type
583 sgWriteShort( fp, 0 ); // nproperties
584 sgWriteShort( fp, 1 ); // nelements
586 sgWriteInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
588 sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
589 sgWritedVec3( fp, center );
590 sgWriteFloat( fp, gbs_radius );
593 sgWriteChar( fp, (char)SG_VERTEX_LIST ); // type
594 sgWriteShort( fp, 0 ); // nproperties
595 sgWriteShort( fp, 1 ); // nelements
596 sgWriteInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
597 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
598 p = wgs84_nodes[i] - gbs_center;
599 sgSetVec3( pt, p.x(), p.y(), p.z() );
600 sgWriteVec3( fp, pt );
603 // dump vertex normal list
604 sgWriteChar( fp, (char)SG_NORMAL_LIST ); // type
605 sgWriteShort( fp, 0 ); // nproperties
606 sgWriteShort( fp, 1 ); // nelements
607 sgWriteInt( fp, normals.size() * 3 ); // nbytes
609 for ( i = 0; i < (int)normals.size(); ++i ) {
611 normal[0] = (char)((p.x() + 1.0) * 128);
612 normal[1] = (char)((p.y() + 1.0) * 128);
613 normal[2] = (char)((p.z() + 1.0) * 128);
614 sgWriteBytes( fp, 3, normal );
617 // dump texture coordinates
618 sgWriteChar( fp, (char)SG_TEXCOORD_LIST ); // type
619 sgWriteShort( fp, 0 ); // nproperties
620 sgWriteShort( fp, 1 ); // nelements
621 sgWriteInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
622 for ( i = 0; i < (int)texcoords.size(); ++i ) {
624 sgSetVec2( t, p.x(), p.y() );
625 sgWriteVec2( fp, t );
628 // dump individual triangles if they exist
629 if ( tris_v.size() > 0 ) {
633 while ( start < (int)tri_materials.size() ) {
635 material = tri_materials[start];
636 while ( (end < (int)tri_materials.size()) &&
637 (material == tri_materials[end]) )
639 // cout << "end = " << end << endl;
642 // cout << "group = " << start << " to " << end - 1 << endl;
644 // write group headers
645 sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
646 sgWriteShort( fp, 1 ); // nproperties
647 sgWriteShort( fp, 1 ); // nelements
649 sgWriteChar( fp, (char)SG_MATERIAL ); // property
650 sgWriteInt( fp, material.length() ); // nbytes
651 sgWriteBytes( fp, material.length(), material.c_str() );
653 sgWriteInt( fp, (end - start) * 3 * 2 * sizeof(short) ); // nbytes
656 for ( i = start; i < end; ++i ) {
657 for ( j = 0; j < 3; ++j ) {
658 sgWriteShort( fp, (short)tris_v[i][j] );
659 sgWriteShort( fp, (short)tris_tc[i][j] );
668 // dump triangle strips
669 if ( strips_v.size() > 0 ) {
673 while ( start < (int)strip_materials.size() ) {
675 material = strip_materials[start];
676 while ( (end < (int)strip_materials.size()) &&
677 (material == strip_materials[end]) )
679 // cout << "end = " << end << endl;
682 // cout << "group = " << start << " to " << end - 1 << endl;
684 // write group headers
685 sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
686 sgWriteShort( fp, 1 ); // nproperties
687 sgWriteShort( fp, end - start ); // nelements
689 sgWriteChar( fp, (char)SG_MATERIAL ); // property
690 sgWriteInt( fp, material.length() ); // nbytes
691 sgWriteBytes( fp, material.length(), material.c_str() );
694 for ( i = start; i < end; ++i ) {
696 sgWriteInt( fp, strips_v[i].size() * 2 * sizeof(short) );
697 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
698 sgWriteShort( fp, (short)strips_v[i][j] );
699 sgWriteShort( fp, (short)strips_tc[i][j] );
708 // dump triangle fans
709 if ( fans_v.size() > 0 ) {
713 while ( start < (int)fan_materials.size() ) {
715 material = fan_materials[start];
716 while ( (end < (int)fan_materials.size()) &&
717 (material == fan_materials[end]) )
719 // cout << "end = " << end << endl;
722 // cout << "group = " << start << " to " << end - 1 << endl;
724 // write group headers
725 sgWriteChar( fp, (char)SG_TRIANGLE_FANS ); // type
726 sgWriteShort( fp, 1 ); // nproperties
727 sgWriteShort( fp, end - start ); // nelements
729 sgWriteChar( fp, (char)SG_MATERIAL ); // property
730 sgWriteInt( fp, material.length() ); // nbytes
731 sgWriteBytes( fp, material.length(), material.c_str() );
734 for ( i = start; i < end; ++i ) {
736 sgWriteInt( fp, fans_v[i].size() * 2 * sizeof(short) );
737 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
738 sgWriteShort( fp, (short)fans_v[i][j] );
739 sgWriteShort( fp, (short)fans_tc[i][j] );
751 if ( sgWriteError() ) {
752 cout << "We detected an error while writing the file." << endl;
760 // write out the structures to an ASCII file. We assume that the
761 // groups come to us sorted by material property. If not, things
762 // don't break, but the result won't be as optimal.
763 bool SGBinObject::write_ascii( const string& base, const string& name,
769 string dir = base + "/" + b.gen_base_path();
770 string command = "mkdir -p " + dir;
772 fg_mkdir( dir.c_str() );
774 system(command.c_str());
777 // string file = dir + "/" + b.gen_index_str();
778 string file = dir + "/" + name;
779 cout << "Output file = " << file << endl;
782 if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
783 cout << "ERROR: opening " << file << " for writing!" << endl;
787 cout << "triangles size = " << tris_v.size() << " tri_materials = "
788 << tri_materials.size() << endl;
789 cout << "strips size = " << strips_v.size() << " strip_materials = "
790 << strip_materials.size() << endl;
791 cout << "fans size = " << fans_v.size() << " fan_materials = "
792 << fan_materials.size() << endl;
794 cout << "points = " << wgs84_nodes.size() << endl;
795 cout << "tex coords = " << texcoords.size() << endl;
797 fprintf(fp, "# FGFS Scenery\n");
798 fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
800 time_t calendar_time = time(NULL);
802 local_tm = localtime( &calendar_time );
804 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
805 fprintf(fp, "# Created %s\n", time_str );
808 // write bounding sphere
809 fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
810 gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
814 fprintf(fp, "# vertex list\n");
815 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
816 p = wgs84_nodes[i] - gbs_center;
818 fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
822 fprintf(fp, "# vertex normal list\n");
823 for ( i = 0; i < (int)normals.size(); ++i ) {
825 fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
829 // dump texture coordinates
830 fprintf(fp, "# texture coordinate list\n");
831 for ( i = 0; i < (int)texcoords.size(); ++i ) {
833 fprintf(fp, "vt %.5f %.5f\n", p.x(), p.y() );
837 // dump individual triangles if they exist
838 if ( tris_v.size() > 0 ) {
839 fprintf(fp, "# triangle groups\n");
844 while ( start < (int)tri_materials.size() ) {
846 material = tri_materials[start];
847 while ( (end < (int)tri_materials.size()) &&
848 (material == tri_materials[end]) )
850 // cout << "end = " << end << endl;
853 // cout << "group = " << start << " to " << end - 1 << endl;
855 // make a list of points for the group
856 point_list group_nodes;
859 double bs_radius = 0;
860 for ( i = start; i < end; ++i ) {
861 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
862 group_nodes.push_back( wgs84_nodes[ tris_v[i][j] ] );
863 bs_center = calc_center( group_nodes );
864 bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
868 // write group headers
870 fprintf(fp, "# usemtl %s\n", material.c_str());
871 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
872 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
875 for ( i = start; i < end; ++i ) {
877 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
878 fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
888 // dump triangle groups
889 if ( strips_v.size() > 0 ) {
890 fprintf(fp, "# triangle strips\n");
895 while ( start < (int)strip_materials.size() ) {
897 material = strip_materials[start];
898 while ( (end < (int)strip_materials.size()) &&
899 (material == strip_materials[end]) )
901 // cout << "end = " << end << endl;
904 // cout << "group = " << start << " to " << end - 1 << endl;
906 // make a list of points for the group
907 point_list group_nodes;
910 double bs_radius = 0;
911 for ( i = start; i < end; ++i ) {
912 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
913 group_nodes.push_back( wgs84_nodes[ strips_v[i][j] ] );
914 bs_center = calc_center( group_nodes );
915 bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
919 // write group headers
921 fprintf(fp, "# usemtl %s\n", material.c_str());
922 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
923 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
926 for ( i = start; i < end; ++i ) {
928 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
929 fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
942 command = "gzip --force --best " + file;
943 system(command.c_str());