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 - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 # include <simgear_config.h>
29 #include <simgear/compiler.h>
30 #include <simgear/debug/logstream.hxx>
35 #include <cstdlib> // for system()
43 #include <simgear/bucket/newbucket.hxx>
44 #include <simgear/misc/sg_path.hxx>
45 #include <simgear/math/SGGeometry.hxx>
46 #include <simgear/structure/exception.hxx>
48 #include "lowlevel.hxx"
49 #include "sg_binobj.hxx"
58 SG_BOUNDING_SPHERE = 0,
65 SG_VA_INTEGER_LIST = 6,
69 SG_TRIANGLE_FACES = 10,
70 SG_TRIANGLE_STRIPS = 11,
75 SG_IDX_VERTICES = 0x01,
76 SG_IDX_NORMALS = 0x02,
78 SG_IDX_TEXCOORDS_0 = 0x08,
79 SG_IDX_TEXCOORDS_1 = 0x10,
80 SG_IDX_TEXCOORDS_2 = 0x20,
81 SG_IDX_TEXCOORDS_3 = 0x40,
84 enum sgVertexAttributeTypes {
86 SG_VA_INTEGER_0 = 0x00000001,
87 SG_VA_INTEGER_1 = 0x00000002,
88 SG_VA_INTEGER_2 = 0x00000004,
89 SG_VA_INTEGER_3 = 0x00000008,
91 SG_VA_FLOAT_0 = 0x00000100,
92 SG_VA_FLOAT_1 = 0x00000200,
93 SG_VA_FLOAT_2 = 0x00000400,
94 SG_VA_FLOAT_3 = 0x00000800,
97 enum sgPropertyTypes {
104 class sgSimpleBuffer {
113 sgSimpleBuffer( unsigned int s = 0) :
126 unsigned int get_size() const { return size; }
127 char *get_ptr() const { return ptr; }
134 void resize( unsigned int s )
148 ptr = new char[size];
154 unsigned int* p = reinterpret_cast<unsigned int*>(ptr + offset);
155 if ( sgIsBigEndian() ) {
156 sgEndianSwap((uint32_t *) p);
159 offset += sizeof(unsigned int);
165 double* p = reinterpret_cast<double*>(ptr + offset);
167 if ( sgIsBigEndian() ) {
168 sgEndianSwap((uint64_t *) p + 0);
169 sgEndianSwap((uint64_t *) p + 1);
170 sgEndianSwap((uint64_t *) p + 2);
173 offset += 3 * sizeof(double);
179 float* p = reinterpret_cast<float*>(ptr + offset);
180 if ( sgIsBigEndian() ) {
181 sgEndianSwap((uint32_t *) p);
184 offset += sizeof(float);
190 float* p = reinterpret_cast<float*>(ptr + offset);
192 if ( sgIsBigEndian() ) {
193 sgEndianSwap((uint32_t *) p + 0);
194 sgEndianSwap((uint32_t *) p + 1);
197 offset += 2 * sizeof(float);
203 float* p = reinterpret_cast<float*>(ptr + offset);
205 if ( sgIsBigEndian() ) {
206 sgEndianSwap((uint32_t *) p + 0);
207 sgEndianSwap((uint32_t *) p + 1);
208 sgEndianSwap((uint32_t *) p + 2);
211 offset += 3 * sizeof(float);
217 float* p = reinterpret_cast<float*>(ptr + offset);
219 if ( sgIsBigEndian() ) {
220 sgEndianSwap((uint32_t *) p + 0);
221 sgEndianSwap((uint32_t *) p + 1);
222 sgEndianSwap((uint32_t *) p + 2);
223 sgEndianSwap((uint32_t *) p + 3);
226 offset += 4 * sizeof(float);
232 static void read_indices(char* buffer,
243 const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
244 const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
245 const int count = bytes / (indexSize + vaSize);
247 // fix endian-ness of the whole lot, if required
248 if (sgIsBigEndian()) {
249 int indices = bytes / sizeof(T);
250 T* src = reinterpret_cast<T*>(buffer);
251 for (int i=0; i<indices; ++i) {
256 T* src = reinterpret_cast<T*>(buffer);
257 for (int i=0; i<count; ++i) {
258 if (indexMask & SG_IDX_VERTICES) vertices.push_back(*src++);
259 if (indexMask & SG_IDX_NORMALS) normals.push_back(*src++);
260 if (indexMask & SG_IDX_COLORS) colors.push_back(*src++);
261 if (indexMask & SG_IDX_TEXCOORDS_0) texCoords[0].push_back(*src++);
262 if (indexMask & SG_IDX_TEXCOORDS_1) texCoords[1].push_back(*src++);
263 if (indexMask & SG_IDX_TEXCOORDS_2) texCoords[2].push_back(*src++);
264 if (indexMask & SG_IDX_TEXCOORDS_3) texCoords[3].push_back(*src++);
267 if (vaMask & SG_VA_INTEGER_0) vas[0].push_back(*src++);
268 if (vaMask & SG_VA_INTEGER_1) vas[1].push_back(*src++);
269 if (vaMask & SG_VA_INTEGER_2) vas[2].push_back(*src++);
270 if (vaMask & SG_VA_INTEGER_3) vas[3].push_back(*src++);
271 if (vaMask & SG_VA_FLOAT_0) vas[4].push_back(*src++);
272 if (vaMask & SG_VA_FLOAT_1) vas[5].push_back(*src++);
273 if (vaMask & SG_VA_FLOAT_2) vas[6].push_back(*src++);
274 if (vaMask & SG_VA_FLOAT_3) vas[7].push_back(*src++);
276 } // of elements in the index
278 // WS2.0 fix : toss zero area triangles
279 if ( ( count == 3 ) && (indexMask & SG_IDX_VERTICES) ) {
280 if ( (vertices[0] == vertices[1]) ||
281 (vertices[1] == vertices[2]) ||
282 (vertices[2] == vertices[0]) ) {
289 void write_indice(gzFile fp, T value)
291 sgWriteBytes(fp, sizeof(T), &value);
294 // specialize template to call endian-aware conversion methods
296 void write_indice(gzFile fp, uint16_t value)
298 sgWriteUShort(fp, value);
302 void write_indice(gzFile fp, uint32_t value)
304 sgWriteUInt(fp, value);
309 void write_indices(gzFile fp,
310 unsigned char indexMask,
312 const int_list& vertices,
313 const int_list& normals,
314 const int_list& colors,
315 const tci_list& texCoords,
316 const vai_list& vas )
318 unsigned int count = vertices.size();
319 const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
320 const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
321 sgWriteUInt(fp, (indexSize + vaSize) * count);
323 for (unsigned int i=0; i < count; ++i) {
324 write_indice(fp, static_cast<T>(vertices[i]));
326 if (indexMask & SG_IDX_NORMALS) {
327 write_indice(fp, static_cast<T>(normals[i]));
329 if (indexMask & SG_IDX_COLORS) {
330 write_indice(fp, static_cast<T>(colors[i]));
332 if (indexMask & SG_IDX_TEXCOORDS_0) {
333 write_indice(fp, static_cast<T>(texCoords[0][i]));
335 if (indexMask & SG_IDX_TEXCOORDS_1) {
336 write_indice(fp, static_cast<T>(texCoords[1][i]));
338 if (indexMask & SG_IDX_TEXCOORDS_2) {
339 write_indice(fp, static_cast<T>(texCoords[2][i]));
341 if (indexMask & SG_IDX_TEXCOORDS_3) {
342 write_indice(fp, static_cast<T>(texCoords[3][i]));
346 if (vaMask & SG_VA_INTEGER_0) {
347 write_indice(fp, static_cast<T>(vas[0][i]));
349 if (vaMask & SG_VA_INTEGER_1) {
350 write_indice(fp, static_cast<T>(vas[1][i]));
352 if (vaMask & SG_VA_INTEGER_2) {
353 write_indice(fp, static_cast<T>(vas[2][i]));
355 if (vaMask & SG_VA_INTEGER_3) {
356 write_indice(fp, static_cast<T>(vas[3][i]));
359 if (vaMask & SG_VA_FLOAT_0) {
360 write_indice(fp, static_cast<T>(vas[4][i]));
362 if (vaMask & SG_VA_FLOAT_1) {
363 write_indice(fp, static_cast<T>(vas[5][i]));
365 if (vaMask & SG_VA_FLOAT_2) {
366 write_indice(fp, static_cast<T>(vas[6][i]));
368 if (vaMask & SG_VA_FLOAT_3) {
369 write_indice(fp, static_cast<T>(vas[7][i]));
376 // read object properties
377 void SGBinObject::read_object( gzFile fp,
381 group_list& vertices,
384 group_tci_list& texCoords,
385 group_vai_list& vertexAttribs,
386 string_list& materials)
389 unsigned char idx_mask;
390 unsigned int vertex_attrib_mask;
392 sgSimpleBuffer buf( 32768 ); // 32 Kb
396 if ( obj_type == SG_POINTS ) {
397 idx_mask = SG_IDX_VERTICES;
399 idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
401 vertex_attrib_mask = 0;
403 for ( j = 0; j < nproperties; ++j ) {
405 sgReadChar( fp, &prop_type );
406 sgReadUInt( fp, &nbytes );
409 char *ptr = buf.get_ptr();
414 sgReadBytes( fp, nbytes, ptr );
418 strncpy( material, ptr, nbytes );
419 material[nbytes] = '\0';
424 sgReadChar( fp, (char *)&idx_mask );
426 sgReadBytes( fp, nbytes, ptr );
430 case SG_VERT_ATTRIBS:
432 sgReadUInt( fp, &vertex_attrib_mask );
434 sgReadBytes( fp, nbytes, ptr );
439 sgReadBytes( fp, nbytes, ptr );
440 SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
445 if ( sgReadError() ) {
446 throw sg_exception("Error reading object properties");
449 size_t indexCount = std::bitset<32>((int)idx_mask).count();
450 if (indexCount == 0) {
451 throw sg_exception("object index mask has no bits set");
454 for ( j = 0; j < nelements; ++j ) {
455 sgReadUInt( fp, &nbytes );
456 if ( sgReadError() ) {
457 throw sg_exception("Error reading element size");
460 buf.resize( nbytes );
461 char *ptr = buf.get_ptr();
462 sgReadBytes( fp, nbytes, ptr );
464 if ( sgReadError() ) {
465 throw sg_exception("Error reading element bytes");
475 read_indices<uint32_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
477 read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
480 // Fix for WS2.0 - ignore zero area triangles
482 vertices.push_back( vs );
483 normals.push_back( ns );
484 colors.push_back( cs );
485 texCoords.push_back( tcs );
486 vertexAttribs.push_back( vas );
487 materials.push_back( material );
489 } // of element iteration
493 // read a binary file and populate the provided structures.
494 bool SGBinObject::read_bin( const string& file ) {
499 sgSimpleBuffer buf( 32768 ); // 32 Kb
501 // zero out structures
502 gbs_center = SGVec3d(0, 0, 0);
514 pt_materials.clear();
521 tri_materials.clear();
528 strip_materials.clear();
535 fan_materials.clear();
538 if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
539 string filegz = file + ".gz";
540 if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
541 SG_LOG( SG_EVENT, SG_ALERT,
542 "ERROR: opening " << file << " or " << filegz << " for reading!");
544 throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
552 sgReadUInt( fp, &header );
553 if ( ((header & 0xFF000000) >> 24) == 'S' &&
554 ((header & 0x00FF0000) >> 16) == 'G' ) {
557 version = (header & 0x0000FFFF);
559 // close the file before we return
561 throw sg_io_exception("Bad BTG magic/version", sg_location(file));
564 // read creation time
565 unsigned int foo_calendar_time;
566 sgReadUInt( fp, &foo_calendar_time );
569 time_t calendar_time = foo_calendar_time;
570 // The following code has a global effect on the host application
571 // and can screws up the time elsewhere. It should be avoided
572 // unless you need this for debugging in which case you should
573 // disable it again once the debugging task is finished.
575 local_tm = localtime( &calendar_time );
577 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
578 SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str);
581 // read number of top level objects
583 if ( version >= 10) { // version 10 extends everything to be 32-bit
584 sgReadInt( fp, &nobjects );
585 } else if ( version >= 7 ) {
587 sgReadUShort( fp, &v );
591 sgReadShort( fp, &v );
595 SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
597 if ( sgReadError() ) {
598 throw sg_io_exception("Error reading BTG file header", sg_location(file));
602 for ( i = 0; i < nobjects; ++i ) {
603 // read object header
605 uint32_t nproperties, nelements;
606 sgReadChar( fp, &obj_type );
607 if ( version >= 10 ) {
608 sgReadUInt( fp, &nproperties );
609 sgReadUInt( fp, &nelements );
610 } else if ( version >= 7 ) {
612 sgReadUShort( fp, &v );
614 sgReadUShort( fp, &v );
618 sgReadShort( fp, &v );
620 sgReadShort( fp, &v );
624 SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
625 " = " << (int)obj_type << " props = " << nproperties <<
626 " elements = " << nelements);
628 if ( obj_type == SG_BOUNDING_SPHERE ) {
629 // read bounding sphere properties
630 read_properties( fp, nproperties );
632 // read bounding sphere elements
633 for ( j = 0; j < nelements; ++j ) {
634 sgReadUInt( fp, &nbytes );
635 buf.resize( nbytes );
637 char *ptr = buf.get_ptr();
638 sgReadBytes( fp, nbytes, ptr );
639 gbs_center = buf.readVec3d();
640 gbs_radius = buf.readFloat();
642 } else if ( obj_type == SG_VERTEX_LIST ) {
643 // read vertex list properties
644 read_properties( fp, nproperties );
646 // read vertex list elements
647 for ( j = 0; j < nelements; ++j ) {
648 sgReadUInt( fp, &nbytes );
649 buf.resize( nbytes );
651 char *ptr = buf.get_ptr();
652 sgReadBytes( fp, nbytes, ptr );
653 int count = nbytes / (sizeof(float) * 3);
654 wgs84_nodes.reserve( count );
655 for ( k = 0; k < count; ++k ) {
656 SGVec3f v = buf.readVec3f();
657 // extend from float to double, hmmm
658 wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
661 } else if ( obj_type == SG_COLOR_LIST ) {
662 // read color list properties
663 read_properties( fp, nproperties );
665 // read color list elements
666 for ( j = 0; j < nelements; ++j ) {
667 sgReadUInt( fp, &nbytes );
668 buf.resize( nbytes );
670 char *ptr = buf.get_ptr();
671 sgReadBytes( fp, nbytes, ptr );
672 int count = nbytes / (sizeof(float) * 4);
673 colors.reserve(count);
674 for ( k = 0; k < count; ++k ) {
675 colors.push_back( buf.readVec4f() );
678 } else if ( obj_type == SG_NORMAL_LIST ) {
679 // read normal list properties
680 read_properties( fp, nproperties );
682 // read normal list elements
683 for ( j = 0; j < nelements; ++j ) {
684 sgReadUInt( fp, &nbytes );
685 buf.resize( nbytes );
687 unsigned char *ptr = (unsigned char *)(buf.get_ptr());
688 sgReadBytes( fp, nbytes, ptr );
689 int count = nbytes / 3;
690 normals.reserve( count );
692 for ( k = 0; k < count; ++k ) {
693 SGVec3f normal( (ptr[0]) / 127.5 - 1.0,
694 (ptr[1]) / 127.5 - 1.0,
695 (ptr[2]) / 127.5 - 1.0);
696 normals.push_back(normalize(normal));
700 } else if ( obj_type == SG_TEXCOORD_LIST ) {
701 // read texcoord list properties
702 read_properties( fp, nproperties );
704 // read texcoord list elements
705 for ( j = 0; j < nelements; ++j ) {
706 sgReadUInt( fp, &nbytes );
707 buf.resize( nbytes );
709 char *ptr = buf.get_ptr();
710 sgReadBytes( fp, nbytes, ptr );
711 int count = nbytes / (sizeof(float) * 2);
712 texcoords.reserve(count);
713 for ( k = 0; k < count; ++k ) {
714 texcoords.push_back( buf.readVec2f() );
717 } else if ( obj_type == SG_VA_FLOAT_LIST ) {
718 // read vertex attribute (float) properties
719 read_properties( fp, nproperties );
721 // read vertex attribute list elements
722 for ( j = 0; j < nelements; ++j ) {
723 sgReadUInt( fp, &nbytes );
724 buf.resize( nbytes );
726 char *ptr = buf.get_ptr();
727 sgReadBytes( fp, nbytes, ptr );
728 int count = nbytes / (sizeof(float));
729 va_flt.reserve(count);
730 for ( k = 0; k < count; ++k ) {
731 va_flt.push_back( buf.readFloat() );
734 } else if ( obj_type == SG_VA_INTEGER_LIST ) {
735 // read vertex attribute (integer) properties
736 read_properties( fp, nproperties );
738 // read vertex attribute list elements
739 for ( j = 0; j < nelements; ++j ) {
740 sgReadUInt( fp, &nbytes );
741 buf.resize( nbytes );
743 char *ptr = buf.get_ptr();
744 sgReadBytes( fp, nbytes, ptr );
745 int count = nbytes / (sizeof(unsigned int));
746 va_int.reserve(count);
747 for ( k = 0; k < count; ++k ) {
748 va_int.push_back( buf.readInt() );
751 } else if ( obj_type == SG_POINTS ) {
752 // read point elements
753 read_object( fp, SG_POINTS, nproperties, nelements,
754 pts_v, pts_n, pts_c, pts_tcs,
755 pts_vas, pt_materials );
756 } else if ( obj_type == SG_TRIANGLE_FACES ) {
757 // read triangle face properties
758 read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
759 tris_v, tris_n, tris_c, tris_tcs,
760 tris_vas, tri_materials );
761 } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
762 // read triangle strip properties
763 read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
764 strips_v, strips_n, strips_c, strips_tcs,
765 strips_vas, strip_materials );
766 } else if ( obj_type == SG_TRIANGLE_FANS ) {
767 // read triangle fan properties
768 read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
769 fans_v, fans_n, fans_c, fans_tcs,
770 fans_vas, fan_materials );
772 // unknown object type, just skip
773 read_properties( fp, nproperties );
776 for ( j = 0; j < nelements; ++j ) {
777 sgReadUInt( fp, &nbytes );
778 // cout << "element size = " << nbytes << endl;
779 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
780 char *ptr = buf.get_ptr();
781 sgReadBytes( fp, nbytes, ptr );
785 if ( sgReadError() ) {
786 throw sg_io_exception("Error while reading object", sg_location(file, i));
796 void SGBinObject::write_header(gzFile fp, int type, int nProps, int nElements)
798 sgWriteChar(fp, (unsigned char) type);
800 sgWriteUShort(fp, nProps);
801 sgWriteUShort(fp, nElements);
803 sgWriteUInt(fp, nProps);
804 sgWriteUInt(fp, nElements);
808 unsigned int SGBinObject::count_objects(const string_list& materials)
810 unsigned int result = 0;
811 unsigned int start = 0, end = 1;
812 unsigned int count = materials.size();
815 while ( start < count ) {
816 m = materials[start];
817 for (end = start+1; (end < count) && (m == materials[end]); ++end) { }
825 void SGBinObject::write_objects(gzFile fp, int type,
826 const group_list& verts,
827 const group_list& normals,
828 const group_list& colors,
829 const group_tci_list& texCoords,
830 const group_vai_list& vertexAttribs,
831 const string_list& materials)
837 unsigned int start = 0, end = 1;
841 while (start < materials.size()) {
842 m = materials[start];
843 // find range of objects with identical material, write out as a single object
844 for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
846 // calc the number of elements
847 const int count = end - start;
849 // calc the number of properties
850 unsigned int va_mask = 0;
851 unsigned int va_count = vertexAttribs.size();
852 for ( unsigned int va=0; va<va_count; va++ ) {
853 if ( !vertexAttribs[va].empty() && !vertexAttribs[va].front().empty() ) {
854 va_mask |= ( 1 << va );
859 write_header(fp, type, 3, count);
861 write_header(fp, type, 2, count);
866 sgWriteChar( fp, (char)SG_MATERIAL ); // property
867 sgWriteUInt( fp, m.length() ); // nbytes
868 sgWriteBytes( fp, m.length(), m.c_str() );
870 // index mask property
871 unsigned char idx_mask = 0;
872 if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
873 if ( !normals.empty() && !normals[start].empty()) idx_mask |= SG_IDX_NORMALS;
874 if ( !colors.empty() && !colors[start].empty()) idx_mask |= SG_IDX_COLORS;
875 if ( !texCoords.empty() && !texCoords[start][0].empty()) idx_mask |= SG_IDX_TEXCOORDS_0;
876 if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
877 if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
878 if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
881 SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
882 << m << "has no indices set");
885 sgWriteChar( fp, (char)SG_INDEX_TYPES ); // property
886 sgWriteUInt( fp, 1 ); // nbytes
887 sgWriteChar( fp, idx_mask );
889 // vertex attribute property
891 sgWriteChar( fp, (char)SG_VERT_ATTRIBS ); // property
892 sgWriteUInt( fp, 4 ); // nbytes
893 sgWriteChar( fp, va_mask );
897 for (unsigned int i=start; i < end; ++i) {
898 const int_list& va(verts[i]);
899 const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
900 const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
902 // pass the whole texcoord array - we'll figure out which indicies to write
904 const tci_list& tca( texCoords[i] );
906 // pass the whole vertex array - we'll figure out which indicies to write
908 const vai_list& vaa( vertexAttribs[i] );
911 write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
913 write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
918 } // of materials iteration
921 // write out the structures to a binary file. We assume that the
922 // groups come to us sorted by material property. If not, things
923 // don't break, but the result won't be as optimal.
924 bool SGBinObject::write_bin( const string& base, const string& name,
928 SGPath file = base + "/" + b.gen_base_path() + "/" + name + ".gz";
929 return write_bin_file(file);
932 static unsigned int max_object_size( const string_list& materials )
934 unsigned int max_size = 0;
936 for (unsigned int start=0; start < materials.size();) {
937 string m = materials[start];
938 unsigned int end = start + 1;
939 // find range of objects with identical material, calc its size
940 for (; (end < materials.size()) && (m == materials[end]); ++end) {}
942 unsigned int cur_size = end - start;
943 max_size = std::max(max_size, cur_size);
950 const unsigned int VERSION_7_MATERIAL_LIMIT = 0x7fff;
952 bool SGBinObject::write_bin_file(const SGPath& file)
957 file2.create_dir( 0755 );
960 if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
961 cout << "ERROR: opening " << file.str() << " for writing!" << endl;
967 SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
968 << " pt_materials = " << pt_materials.size() );
969 SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
970 << " tri_materials = " << tri_materials.size() );
971 SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
972 << " strip_materials = " << strip_materials.size() );
973 SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
974 << " fan_materials = " << fan_materials.size() );
976 SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
977 SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
978 SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
979 SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
982 bool shortMaterialsRanges =
983 (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) &&
984 (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) &&
985 (max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) &&
986 (max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT);
988 if ((wgs84_nodes.size() < 0xffff) &&
989 (normals.size() < 0xffff) &&
990 (texcoords.size() < 0xffff) &&
991 shortMaterialsRanges) {
992 version = 7; // use smaller indices if possible
995 // write header magic
997 /** Magic Number for our file format */
998 #define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version )
1000 sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
1001 time_t calendar_time = time(NULL);
1002 sgWriteLong( fp, (int32_t)calendar_time );
1004 // calculate and write number of top level objects
1005 int nobjects = 5; // gbs, vertices, colors, normals, texcoords
1006 nobjects += count_objects(pt_materials);
1007 nobjects += count_objects(tri_materials);
1008 nobjects += count_objects(strip_materials);
1009 nobjects += count_objects(fan_materials);
1011 SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
1014 sgWriteUShort( fp, (uint16_t) nobjects );
1016 sgWriteInt( fp, nobjects );
1019 // write bounding sphere
1020 write_header( fp, SG_BOUNDING_SPHERE, 0, 1);
1021 sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
1022 sgWritedVec3( fp, gbs_center );
1023 sgWriteFloat( fp, gbs_radius );
1026 write_header( fp, SG_VERTEX_LIST, 0, 1);
1027 sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
1028 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1029 sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center));
1032 // dump vertex color list
1033 write_header( fp, SG_COLOR_LIST, 0, 1);
1034 sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes
1035 for ( i = 0; i < (int)colors.size(); ++i ) {
1036 sgWriteVec4( fp, colors[i]);
1039 // dump vertex normal list
1040 write_header( fp, SG_NORMAL_LIST, 0, 1);
1041 sgWriteUInt( fp, normals.size() * 3 ); // nbytes
1043 for ( i = 0; i < (int)normals.size(); ++i ) {
1044 SGVec3f p = normals[i];
1045 normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
1046 normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
1047 normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
1048 sgWriteBytes( fp, 3, normal );
1051 // dump texture coordinates
1052 write_header( fp, SG_TEXCOORD_LIST, 0, 1);
1053 sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
1054 for ( i = 0; i < (int)texcoords.size(); ++i ) {
1055 sgWriteVec2( fp, texcoords[i]);
1058 write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
1059 write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
1060 write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
1061 write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
1066 if ( sgWriteError() ) {
1067 cout << "Error while writing file " << file.str() << endl;
1075 // write out the structures to an ASCII file. We assume that the
1076 // groups come to us sorted by material property. If not, things
1077 // don't break, but the result won't be as optimal.
1078 bool SGBinObject::write_ascii( const string& base, const string& name,
1083 SGPath file = base + "/" + b.gen_base_path() + "/" + name;
1084 file.create_dir( 0755 );
1085 cout << "Output file = " << file.str() << endl;
1088 if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
1089 cout << "ERROR: opening " << file.str() << " for writing!" << endl;
1093 cout << "triangles size = " << tris_v.size() << " tri_materials = "
1094 << tri_materials.size() << endl;
1095 cout << "strips size = " << strips_v.size() << " strip_materials = "
1096 << strip_materials.size() << endl;
1097 cout << "fans size = " << fans_v.size() << " fan_materials = "
1098 << fan_materials.size() << endl;
1100 cout << "points = " << wgs84_nodes.size() << endl;
1101 cout << "tex coords = " << texcoords.size() << endl;
1103 fprintf(fp, "# FGFS Scenery\n");
1104 fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
1106 time_t calendar_time = time(NULL);
1107 struct tm *local_tm;
1108 local_tm = localtime( &calendar_time );
1110 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
1111 fprintf(fp, "# Created %s\n", time_str );
1114 // write bounding sphere
1115 fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
1116 gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
1120 fprintf(fp, "# vertex list\n");
1121 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1122 SGVec3d p = wgs84_nodes[i] - gbs_center;
1124 fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1128 fprintf(fp, "# vertex normal list\n");
1129 for ( i = 0; i < (int)normals.size(); ++i ) {
1130 SGVec3f p = normals[i];
1131 fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1135 // dump texture coordinates
1136 fprintf(fp, "# texture coordinate list\n");
1137 for ( i = 0; i < (int)texcoords.size(); ++i ) {
1138 SGVec2f p = texcoords[i];
1139 fprintf(fp, "vt %.5f %.5f\n", p.x(), p.y() );
1143 // dump individual triangles if they exist
1144 if ( ! tris_v.empty() ) {
1145 fprintf(fp, "# triangle groups\n");
1150 while ( start < (int)tri_materials.size() ) {
1152 material = tri_materials[start];
1153 while ( (end < (int)tri_materials.size()) &&
1154 (material == tri_materials[end]) )
1156 // cout << "end = " << end << endl;
1159 // cout << "group = " << start << " to " << end - 1 << endl;
1161 SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
1162 for ( i = start; i < end; ++i ) {
1163 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1164 d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
1168 SGVec3d bs_center = d.getCenter();
1169 double bs_radius = d.getRadius();
1171 // write group headers
1173 fprintf(fp, "# usemtl %s\n", material.c_str());
1174 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1175 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1178 for ( i = start; i < end; ++i ) {
1180 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1181 fprintf(fp, " %d/%d", tris_v[i][j], tris_tcs[i][0][j] );
1191 // dump triangle groups
1192 if ( ! strips_v.empty() ) {
1193 fprintf(fp, "# triangle strips\n");
1198 while ( start < (int)strip_materials.size() ) {
1200 material = strip_materials[start];
1201 while ( (end < (int)strip_materials.size()) &&
1202 (material == strip_materials[end]) )
1204 // cout << "end = " << end << endl;
1207 // cout << "group = " << start << " to " << end - 1 << endl;
1210 SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
1211 for ( i = start; i < end; ++i ) {
1212 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1213 d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
1217 SGVec3d bs_center = d.getCenter();
1218 double bs_radius = d.getRadius();
1220 // write group headers
1222 fprintf(fp, "# usemtl %s\n", material.c_str());
1223 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1224 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1227 for ( i = start; i < end; ++i ) {
1229 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1230 fprintf(fp, " %d/%d", strips_v[i][j], strips_tcs[i][0][j] );
1243 string command = "gzip --force --best " + file.str();
1244 int err = system(command.c_str());
1247 cout << "ERROR: gzip " << file.str() << " failed!" << endl;
1253 void SGBinObject::read_properties(gzFile fp, int nproperties)
1259 for ( int j = 0; j < nproperties; ++j ) {
1261 sgReadChar( fp, &prop_type );
1262 sgReadUInt( fp, &nbytes );
1263 // cout << "property size = " << nbytes << endl;
1264 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
1265 char *ptr = buf.get_ptr();
1266 sgReadBytes( fp, nbytes, ptr );
1270 bool SGBinObject::add_point( const SGBinObjectPoint& pt )
1272 // add the point info
1273 pt_materials.push_back( pt.material );
1275 pts_v.push_back( pt.v_list );
1276 pts_n.push_back( pt.n_list );
1277 pts_c.push_back( pt.c_list );
1282 bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
1284 // add the triangle info and keep lists aligned
1285 tri_materials.push_back( tri.material );
1286 tris_v.push_back( tri.v_list );
1287 tris_n.push_back( tri.n_list );
1288 tris_c.push_back( tri.c_list );
1289 tris_tcs.push_back( tri.tc_list );
1290 tris_vas.push_back( tri.va_list );