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 // write out the structures to an ASCII file. We assume that the
111 // groups come to us sorted by material property. If not, things
112 // don't break, but the result won't be as optimal.
113 bool sgWriteAsciiObj( const string& base, const string& name, const SGBucket& b,
114 Point3D gbs_center, float gbs_radius,
115 const point_list& wgs84_nodes, const point_list& normals,
116 const point_list& texcoords,
117 const group_list& tris_v, const group_list& tris_tc,
118 const string_list& tri_materials,
119 const group_list& strips_v, const group_list& strips_tc,
120 const string_list& strip_materials,
121 const group_list& fans_v, const group_list& fans_tc,
122 const string_list& fan_materials )
127 string dir = base + "/" + b.gen_base_path();
128 string command = "mkdir -p " + dir;
130 fg_mkdir( dir.c_str() );
132 system(command.c_str());
135 // string file = dir + "/" + b.gen_index_str();
136 string file = dir + "/" + name;
137 cout << "Output file = " << file << endl;
140 if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
141 cout << "ERROR: opening " << file << " for writing!" << endl;
145 cout << "triangles size = " << tris_v.size() << " tri_materials = "
146 << tri_materials.size() << endl;
147 cout << "strips size = " << strips_v.size() << " strip_materials = "
148 << strip_materials.size() << endl;
149 cout << "fans size = " << fans_v.size() << " fan_materials = "
150 << fan_materials.size() << endl;
152 cout << "points = " << wgs84_nodes.size() << endl;
153 cout << "tex coords = " << texcoords.size() << endl;
155 fprintf(fp, "# FGFS Scenery\n");
156 fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
158 time_t calendar_time = time(NULL);
160 local_tm = localtime( &calendar_time );
162 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
163 fprintf(fp, "# Created %s\n", time_str );
166 // write bounding sphere
167 fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
168 gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
172 fprintf(fp, "# vertex list\n");
173 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
174 p = wgs84_nodes[i] - gbs_center;
176 fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
180 fprintf(fp, "# vertex normal list\n");
181 for ( i = 0; i < (int)normals.size(); ++i ) {
183 fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
187 // dump texture coordinates
188 fprintf(fp, "# texture coordinate list\n");
189 for ( i = 0; i < (int)texcoords.size(); ++i ) {
191 fprintf(fp, "vt %.5f %.5f\n", p.x(), p.y() );
195 // dump individual triangles if they exist
196 if ( tris_v.size() > 0 ) {
197 fprintf(fp, "# triangle groups\n");
202 while ( start < (int)tri_materials.size() ) {
204 material = tri_materials[start];
205 while ( (end < (int)tri_materials.size()) &&
206 (material == tri_materials[end]) )
208 // cout << "end = " << end << endl;
211 // cout << "group = " << start << " to " << end - 1 << endl;
213 // make a list of points for the group
214 point_list group_nodes;
217 double bs_radius = 0;
218 for ( i = start; i < end; ++i ) {
219 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
220 group_nodes.push_back( wgs84_nodes[ tris_v[i][j] ] );
221 bs_center = calc_center( group_nodes );
222 bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
226 // write group headers
228 fprintf(fp, "# usemtl %s\n", material.c_str());
229 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
230 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
233 for ( i = start; i < end; ++i ) {
235 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
236 fprintf(fp, " %d/%d", tris_v[i][j], tris_tc[i][j] );
246 // dump triangle groups
247 if ( strips_v.size() > 0 ) {
248 fprintf(fp, "# triangle strips\n");
253 while ( start < (int)strip_materials.size() ) {
255 material = strip_materials[start];
256 while ( (end < (int)strip_materials.size()) &&
257 (material == strip_materials[end]) )
259 // cout << "end = " << end << endl;
262 // cout << "group = " << start << " to " << end - 1 << endl;
264 // make a list of points for the group
265 point_list group_nodes;
268 double bs_radius = 0;
269 for ( i = start; i < end; ++i ) {
270 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
271 group_nodes.push_back( wgs84_nodes[ strips_v[i][j] ] );
272 bs_center = calc_center( group_nodes );
273 bs_radius = sgCalcBoundingRadius( bs_center, group_nodes );
277 // write group headers
279 fprintf(fp, "# usemtl %s\n", material.c_str());
280 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
281 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
284 for ( i = start; i < end; ++i ) {
286 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
287 fprintf(fp, " %d/%d", strips_v[i][j], strips_tc[i][j] );
300 command = "gzip --force --best " + file;
301 system(command.c_str());
307 // read a binary file and populate the provided structures.
308 bool sgReadBinObj( const string& file,
309 Point3D &gbs_center, float *gbs_radius,
310 point_list& wgs84_nodes, point_list& normals,
311 point_list& texcoords,
312 group_list& tris_v, group_list& tris_tc,
313 string_list& tri_materials,
314 group_list& strips_v, group_list& strips_tc,
315 string_list& strip_materials,
316 group_list& fans_v, group_list& fans_tc,
317 string_list& fan_materials )
323 // zero out structures
330 tri_materials.clear();
334 strip_materials.clear();
338 fan_materials.clear();
340 cout << "Loading binary input file = " << file << endl;
343 if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
344 string filegz = file + ".gz";
345 if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
346 cout << "ERROR: opening " << file << " or " << filegz
347 << "for reading!" << endl;
356 sgReadInt( fp, &header );
357 if ( ((header & 0xFF000000) >> 24) == 'S' &&
358 ((header & 0x00FF0000) >> 16) == 'G' ) {
359 cout << "Good header" << endl;
361 version = (header & 0x0000FFFF);
362 cout << "File version = " << version << endl;
367 // read creation time
368 time_t calendar_time;
369 sgReadLong( fp, &calendar_time );
371 local_tm = localtime( &calendar_time );
373 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
374 cout << "File created on " << time_str << endl;
376 // read number of top level objects
378 sgReadShort( fp, &nobjects );
379 cout << "Total objects to read = " << nobjects << endl;
382 for ( i = 0; i < nobjects; ++i ) {
383 // read object header
385 short nproperties, nelements;
386 sgReadChar( fp, &obj_type );
387 sgReadShort( fp, &nproperties );
388 sgReadShort( fp, &nelements );
390 cout << "object " << i << " = " << (int)obj_type << " props = "
391 << nproperties << " elements = " << nelements << endl;
393 if ( obj_type == SG_BOUNDING_SPHERE ) {
394 // read bounding sphere properties
395 for ( j = 0; j < nproperties; ++j ) {
397 sgReadChar( fp, &prop_type );
400 sgReadInt( fp, &nbytes );
401 cout << "property size = " << nbytes << endl;
404 sgReadBytes( fp, nbytes, ptr );
407 // read bounding sphere elements
408 for ( j = 0; j < nelements; ++j ) {
410 sgReadInt( fp, &nbytes );
411 cout << "element size = " << nbytes << endl;
414 sgReadBytes( fp, nbytes, ptr );
416 double *dptr = (double *)ptr;
417 gbs_center = Point3D( dptr[0], dptr[1], dptr[2] );
418 cout << "Center = " << gbs_center << endl;
419 ptr += sizeof(double) * 3;
421 float *fptr = (float *)ptr;
422 *gbs_radius = fptr[0];
423 cout << "Bounding radius = " << *gbs_radius << endl;
425 } else if ( obj_type == SG_VERTEX_LIST ) {
426 // read vertex list properties
427 for ( j = 0; j < nproperties; ++j ) {
429 sgReadChar( fp, &prop_type );
432 sgReadInt( fp, &nbytes );
433 cout << "property size = " << nbytes << endl;
436 sgReadBytes( fp, nbytes, ptr );
439 // read vertex list elements
440 for ( j = 0; j < nelements; ++j ) {
442 sgReadInt( fp, &nbytes );
443 cout << "element size = " << nbytes << endl;
446 sgReadBytes( fp, nbytes, ptr );
447 int count = nbytes / (sizeof(float) * 3);
448 float *fptr = (float *)ptr;
449 for ( k = 0; k < count; ++k ) {
450 p = Point3D( fptr[0], fptr[1], fptr[2] );
451 cout << "node = " << p << endl;
452 wgs84_nodes.push_back( p );
456 } else if ( obj_type == SG_NORMAL_LIST ) {
457 // read normal list properties
458 for ( j = 0; j < nproperties; ++j ) {
460 sgReadChar( fp, &prop_type );
463 sgReadInt( fp, &nbytes );
464 cout << "property size = " << nbytes << endl;
467 sgReadBytes( fp, nbytes, ptr );
470 // read normal list elements
471 for ( j = 0; j < nelements; ++j ) {
473 sgReadInt( fp, &nbytes );
474 cout << "element size = " << nbytes << endl;
477 sgReadBytes( fp, nbytes, ptr );
478 int count = nbytes / 3;
479 for ( k = 0; k < count; ++k ) {
480 p = Point3D( ptr[0] / 128.0 - 1.0,
481 ptr[1] / 128.0 - 1.0,
482 ptr[2] / 128.0 - 1.0 );
483 cout << "normal = " << p << endl;
484 normals.push_back( p );
488 } else if ( obj_type == SG_TEXCOORD_LIST ) {
489 // read texcoord list properties
490 for ( j = 0; j < nproperties; ++j ) {
492 sgReadChar( fp, &prop_type );
495 sgReadInt( fp, &nbytes );
496 cout << "property size = " << nbytes << endl;
499 sgReadBytes( fp, nbytes, ptr );
502 // read texcoord list elements
503 for ( j = 0; j < nelements; ++j ) {
505 sgReadInt( fp, &nbytes );
506 cout << "element size = " << nbytes << endl;
509 sgReadBytes( fp, nbytes, ptr );
510 int count = nbytes / (sizeof(float) * 2);
511 float *fptr = (float *)ptr;
512 for ( k = 0; k < count; ++k ) {
513 p = Point3D( fptr[0], fptr[1], 0 );
514 cout << "texcoord = " << p << endl;
515 texcoords.push_back( p );
519 } else if ( obj_type == SG_TRIANGLE_FACES ) {
520 // read triangle face properties
521 for ( j = 0; j < nproperties; ++j ) {
523 sgReadChar( fp, &prop_type );
526 sgReadInt( fp, &nbytes );
527 cout << "property size = " << nbytes << endl;
530 sgReadBytes( fp, nbytes, ptr );
531 if ( prop_type == SG_MATERIAL ) {
532 strncpy( material, ptr, nbytes );
533 material[nbytes] = '\0';
534 cout << "material type = " << material << endl;
538 // read triangle face elements
539 for ( j = 0; j < nelements; ++j ) {
541 sgReadInt( fp, &nbytes );
542 cout << "element size = " << nbytes << endl;
545 sgReadBytes( fp, nbytes, ptr );
546 int count = nbytes / (sizeof(short) * 2);
547 short *sptr = (short *)ptr;
549 vs.clear(); tcs.clear();
550 for ( k = 0; k < count; ++k ) {
551 vs.push_back( sptr[0] );
552 tcs.push_back( sptr[1] );
553 cout << sptr[0] << "/" << sptr[1] << " ";
557 tris_v.push_back( vs );
558 tris_tc.push_back( tcs );
559 tri_materials.push_back( material );
561 } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
562 // read triangle strip properties
563 for ( j = 0; j < nproperties; ++j ) {
565 sgReadChar( fp, &prop_type );
568 sgReadInt( fp, &nbytes );
569 cout << "property size = " << nbytes << endl;
572 sgReadBytes( fp, nbytes, ptr );
573 if ( prop_type == SG_MATERIAL ) {
574 strncpy( material, ptr, nbytes );
575 material[nbytes] = '\0';
576 cout << "material type = " << material << endl;
580 // read triangle strip elements
581 for ( j = 0; j < nelements; ++j ) {
583 sgReadInt( fp, &nbytes );
584 cout << "element size = " << nbytes << endl;
587 sgReadBytes( fp, nbytes, ptr );
588 int count = nbytes / (sizeof(short) * 2);
589 short *sptr = (short *)ptr;
591 vs.clear(); tcs.clear();
592 for ( k = 0; k < count; ++k ) {
593 vs.push_back( sptr[0] );
594 tcs.push_back( sptr[1] );
595 cout << sptr[0] << "/" << sptr[1] << " ";
599 strips_v.push_back( vs );
600 strips_tc.push_back( tcs );
601 strip_materials.push_back( material );
603 } else if ( obj_type == SG_TRIANGLE_FANS ) {
604 // read triangle fan properties
605 for ( j = 0; j < nproperties; ++j ) {
607 sgReadChar( fp, &prop_type );
610 sgReadInt( fp, &nbytes );
611 cout << "property size = " << nbytes << endl;
614 sgReadBytes( fp, nbytes, ptr );
615 if ( prop_type == SG_MATERIAL ) {
616 strncpy( material, ptr, nbytes );
617 material[nbytes] = '\0';
618 cout << "material type = " << material << endl;
622 // read triangle fan elements
623 for ( j = 0; j < nelements; ++j ) {
625 sgReadInt( fp, &nbytes );
626 cout << "element size = " << nbytes << endl;
629 sgReadBytes( fp, nbytes, ptr );
630 int count = nbytes / (sizeof(short) * 2);
631 short *sptr = (short *)ptr;
633 vs.clear(); tcs.clear();
634 for ( k = 0; k < count; ++k ) {
635 vs.push_back( sptr[0] );
636 tcs.push_back( sptr[1] );
637 cout << sptr[0] << "/" << sptr[1] << " ";
641 fans_v.push_back( vs );
642 fans_tc.push_back( tcs );
643 fan_materials.push_back( material );
646 // unknown object type, just skip
649 for ( j = 0; j < nproperties; ++j ) {
651 sgReadChar( fp, &prop_type );
654 sgReadInt( fp, &nbytes );
655 cout << "property size = " << nbytes << endl;
658 sgReadBytes( fp, nbytes, ptr );
662 for ( j = 0; j < nelements; ++j ) {
664 sgReadInt( fp, &nbytes );
665 cout << "element size = " << nbytes << endl;
668 sgReadBytes( fp, nbytes, ptr );
676 if ( sgReadError() ) {
677 cout << "We detected an error while reading the file." << endl;
685 // write out the structures to a binary file. We assume that the
686 // groups come to us sorted by material property. If not, things
687 // don't break, but the result won't be as optimal.
688 bool sgWriteBinObj( const string& base, const string& name, const SGBucket& b,
689 Point3D gbs_center, float gbs_radius,
690 const point_list& wgs84_nodes, const point_list& normals,
691 const point_list& texcoords,
692 const group_list& tris_v, const group_list& tris_tc,
693 const string_list& tri_materials,
694 const group_list& strips_v, const group_list& strips_tc,
695 const string_list& strip_materials,
696 const group_list& fans_v, const group_list& fans_tc,
697 const string_list& fan_materials )
704 string dir = base + "/" + b.gen_base_path();
705 string command = "mkdir -p " + dir;
707 fg_mkdir( dir.c_str() );
709 system(command.c_str());
712 string file = dir + "/" + name + ".gz";
713 cout << "Output file = " << file << endl;
716 if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
717 cout << "ERROR: opening " << file << " for writing!" << endl;
723 cout << "triangles size = " << tris_v.size() << " tri_materials = "
724 << tri_materials.size() << endl;
725 cout << "strips size = " << strips_v.size() << " strip_materials = "
726 << strip_materials.size() << endl;
727 cout << "fans size = " << fans_v.size() << " fan_materials = "
728 << fan_materials.size() << endl;
730 cout << "points = " << wgs84_nodes.size() << endl;
731 cout << "tex coords = " << texcoords.size() << endl;
733 // write header magic
734 sgWriteInt( fp, SG_FILE_MAGIC_NUMBER );
735 time_t calendar_time = time(NULL);
736 sgWriteLong( fp, (long int)calendar_time );
738 // calculate and write number of top level objects
743 nobjects++; // for gbs
744 nobjects++; // for vertices
745 nobjects++; // for normals
746 nobjects++; // for texcoords
751 while ( start < (int)tri_materials.size() ) {
752 material = tri_materials[start];
753 while ( (end < (int)tri_materials.size()) &&
754 (material == tri_materials[end]) ) {
758 start = end; end = start + 1;
765 while ( start < (int)strip_materials.size() ) {
766 material = strip_materials[start];
767 while ( (end < (int)strip_materials.size()) &&
768 (material == strip_materials[end]) ) {
772 start = end; end = start + 1;
779 while ( start < (int)fan_materials.size() ) {
780 material = fan_materials[start];
781 while ( (end < (int)fan_materials.size()) &&
782 (material == fan_materials[end]) ) {
786 start = end; end = start + 1;
790 cout << "total top level objects = " << nobjects << endl;
791 sgWriteShort( fp, nobjects );
793 // write bounding sphere
794 sgWriteChar( fp, (char)SG_BOUNDING_SPHERE ); // type
795 sgWriteShort( fp, 0 ); // nproperties
796 sgWriteShort( fp, 1 ); // nelements
798 sgWriteInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
800 sgdSetVec3( center, gbs_center.x(), gbs_center.y(), gbs_center.z() );
801 sgWritedVec3( fp, center );
802 sgWriteFloat( fp, gbs_radius );
805 sgWriteChar( fp, (char)SG_VERTEX_LIST ); // type
806 sgWriteShort( fp, 0 ); // nproperties
807 sgWriteShort( fp, 1 ); // nelements
808 sgWriteInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
809 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
810 p = wgs84_nodes[i] - gbs_center;
811 sgSetVec3( pt, p.x(), p.y(), p.z() );
812 sgWriteVec3( fp, pt );
815 // dump vertex normal list
816 sgWriteChar( fp, (char)SG_NORMAL_LIST ); // type
817 sgWriteShort( fp, 0 ); // nproperties
818 sgWriteShort( fp, 1 ); // nelements
819 sgWriteInt( fp, normals.size() * 3 ); // nbytes
821 for ( i = 0; i < (int)normals.size(); ++i ) {
823 normal[0] = (char)((p.x() + 1.0) * 128);
824 normal[1] = (char)((p.y() + 1.0) * 128);
825 normal[2] = (char)((p.z() + 1.0) * 128);
826 sgWriteBytes( fp, 3, normal );
829 // dump texture coordinates
830 sgWriteChar( fp, (char)SG_TEXCOORD_LIST ); // type
831 sgWriteShort( fp, 0 ); // nproperties
832 sgWriteShort( fp, 1 ); // nelements
833 sgWriteInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
834 for ( i = 0; i < (int)texcoords.size(); ++i ) {
836 sgSetVec2( t, p.x(), p.y() );
837 sgWriteVec2( fp, t );
840 // dump individual triangles if they exist
841 if ( tris_v.size() > 0 ) {
845 while ( start < (int)tri_materials.size() ) {
847 material = tri_materials[start];
848 while ( (end < (int)tri_materials.size()) &&
849 (material == tri_materials[end]) )
851 // cout << "end = " << end << endl;
854 // cout << "group = " << start << " to " << end - 1 << endl;
856 // write group headers
857 sgWriteChar( fp, (char)SG_TRIANGLE_FACES ); // type
858 sgWriteShort( fp, 1 ); // nproperties
859 sgWriteShort( fp, 1 ); // nelements
861 sgWriteChar( fp, (char)SG_MATERIAL ); // property
862 sgWriteInt( fp, material.length() ); // nbytes
863 sgWriteBytes( fp, material.length(), material.c_str() );
865 sgWriteInt( fp, (end - start) * 3 * 2 * sizeof(short) ); // nbytes
868 for ( i = start; i < end; ++i ) {
869 for ( j = 0; j < 3; ++j ) {
870 sgWriteShort( fp, (short)tris_v[i][j] );
871 sgWriteShort( fp, (short)tris_tc[i][j] );
880 // dump triangle strips
881 if ( strips_v.size() > 0 ) {
885 while ( start < (int)strip_materials.size() ) {
887 material = strip_materials[start];
888 while ( (end < (int)strip_materials.size()) &&
889 (material == strip_materials[end]) )
891 // cout << "end = " << end << endl;
894 // cout << "group = " << start << " to " << end - 1 << endl;
896 // write group headers
897 sgWriteChar( fp, (char)SG_TRIANGLE_STRIPS ); // type
898 sgWriteShort( fp, 1 ); // nproperties
899 sgWriteShort( fp, end - start ); // nelements
901 sgWriteChar( fp, (char)SG_MATERIAL ); // property
902 sgWriteInt( fp, material.length() ); // nbytes
903 sgWriteBytes( fp, material.length(), material.c_str() );
906 for ( i = start; i < end; ++i ) {
908 sgWriteInt( fp, strips_v[i].size() * 2 * sizeof(short) );
909 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
910 sgWriteShort( fp, (short)strips_v[i][j] );
911 sgWriteShort( fp, (short)strips_tc[i][j] );
920 // dump triangle fans
921 if ( fans_v.size() > 0 ) {
925 while ( start < (int)fan_materials.size() ) {
927 material = fan_materials[start];
928 while ( (end < (int)fan_materials.size()) &&
929 (material == fan_materials[end]) )
931 // cout << "end = " << end << endl;
934 // cout << "group = " << start << " to " << end - 1 << endl;
936 // write group headers
937 sgWriteChar( fp, (char)SG_TRIANGLE_FANS ); // type
938 sgWriteShort( fp, 1 ); // nproperties
939 sgWriteShort( fp, end - start ); // nelements
941 sgWriteChar( fp, (char)SG_MATERIAL ); // property
942 sgWriteInt( fp, material.length() ); // nbytes
943 sgWriteBytes( fp, material.length(), material.c_str() );
946 for ( i = start; i < end; ++i ) {
948 sgWriteInt( fp, fans_v[i].size() * 2 * sizeof(short) );
949 for ( j = 0; j < (int)fans_v[i].size(); ++j ) {
950 sgWriteShort( fp, (short)fans_v[i][j] );
951 sgWriteShort( fp, (short)fans_tc[i][j] );
963 if ( sgWriteError() ) {
964 cout << "We detected an error while writing the file." << endl;