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
280 void write_indice(gzFile fp, T value)
282 sgWriteBytes(fp, sizeof(T), &value);
285 // specialize template to call endian-aware conversion methods
287 void write_indice(gzFile fp, uint16_t value)
289 sgWriteUShort(fp, value);
293 void write_indice(gzFile fp, uint32_t value)
295 sgWriteUInt(fp, value);
300 void write_indices(gzFile fp,
301 unsigned char indexMask,
303 const int_list& vertices,
304 const int_list& normals,
305 const int_list& colors,
306 const tci_list& texCoords,
307 const vai_list& vas )
309 unsigned int count = vertices.size();
310 const int indexSize = sizeof(T) * std::bitset<32>((int)indexMask).count();
311 const int vaSize = sizeof(T) * std::bitset<32>((int)vaMask).count();
312 sgWriteUInt(fp, (indexSize + vaSize) * count);
314 for (unsigned int i=0; i < count; ++i) {
315 write_indice(fp, static_cast<T>(vertices[i]));
317 if (indexMask & SG_IDX_NORMALS) {
318 write_indice(fp, static_cast<T>(normals[i]));
320 if (indexMask & SG_IDX_COLORS) {
321 write_indice(fp, static_cast<T>(colors[i]));
323 if (indexMask & SG_IDX_TEXCOORDS_0) {
324 write_indice(fp, static_cast<T>(texCoords[0][i]));
326 if (indexMask & SG_IDX_TEXCOORDS_1) {
327 write_indice(fp, static_cast<T>(texCoords[1][i]));
329 if (indexMask & SG_IDX_TEXCOORDS_2) {
330 write_indice(fp, static_cast<T>(texCoords[2][i]));
332 if (indexMask & SG_IDX_TEXCOORDS_3) {
333 write_indice(fp, static_cast<T>(texCoords[3][i]));
337 if (vaMask & SG_VA_INTEGER_0) {
338 write_indice(fp, static_cast<T>(vas[0][i]));
340 if (vaMask & SG_VA_INTEGER_1) {
341 write_indice(fp, static_cast<T>(vas[1][i]));
343 if (vaMask & SG_VA_INTEGER_2) {
344 write_indice(fp, static_cast<T>(vas[2][i]));
346 if (vaMask & SG_VA_INTEGER_3) {
347 write_indice(fp, static_cast<T>(vas[3][i]));
350 if (vaMask & SG_VA_FLOAT_0) {
351 write_indice(fp, static_cast<T>(vas[4][i]));
353 if (vaMask & SG_VA_FLOAT_1) {
354 write_indice(fp, static_cast<T>(vas[5][i]));
356 if (vaMask & SG_VA_FLOAT_2) {
357 write_indice(fp, static_cast<T>(vas[6][i]));
359 if (vaMask & SG_VA_FLOAT_3) {
360 write_indice(fp, static_cast<T>(vas[7][i]));
367 // read object properties
368 void SGBinObject::read_object( gzFile fp,
372 group_list& vertices,
375 group_tci_list& texCoords,
376 group_vai_list& vertexAttribs,
377 string_list& materials)
380 unsigned char idx_mask;
381 unsigned int vertex_attrib_mask;
383 sgSimpleBuffer buf( 32768 ); // 32 Kb
387 if ( obj_type == SG_POINTS ) {
388 idx_mask = SG_IDX_VERTICES;
390 idx_mask = (char)(SG_IDX_VERTICES | SG_IDX_TEXCOORDS_0);
392 vertex_attrib_mask = 0;
394 for ( j = 0; j < nproperties; ++j ) {
396 sgReadChar( fp, &prop_type );
397 sgReadUInt( fp, &nbytes );
400 char *ptr = buf.get_ptr();
405 sgReadBytes( fp, nbytes, ptr );
409 strncpy( material, ptr, nbytes );
410 material[nbytes] = '\0';
415 sgReadChar( fp, (char *)&idx_mask );
417 sgReadBytes( fp, nbytes, ptr );
421 case SG_VERT_ATTRIBS:
423 sgReadUInt( fp, &vertex_attrib_mask );
425 sgReadBytes( fp, nbytes, ptr );
430 sgReadBytes( fp, nbytes, ptr );
431 SG_LOG(SG_IO, SG_ALERT, "Found UNKNOWN property type with nbytes == " << nbytes << " mask is " << (int)idx_mask );
436 if ( sgReadError() ) {
437 throw sg_exception("Error reading object properties");
440 size_t indexCount = std::bitset<32>((int)idx_mask).count();
441 if (indexCount == 0) {
442 throw sg_exception("object index mask has no bits set");
445 for ( j = 0; j < nelements; ++j ) {
446 sgReadUInt( fp, &nbytes );
447 if ( sgReadError() ) {
448 throw sg_exception("Error reading element size");
451 buf.resize( nbytes );
452 char *ptr = buf.get_ptr();
453 sgReadBytes( fp, nbytes, ptr );
455 if ( sgReadError() ) {
456 throw sg_exception("Error reading element bytes");
466 read_indices<uint32_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
468 read_indices<uint16_t>(ptr, nbytes, idx_mask, vertex_attrib_mask, vs, ns, cs, tcs, vas );
471 vertices.push_back( vs );
472 normals.push_back( ns );
473 colors.push_back( cs );
474 texCoords.push_back( tcs );
475 vertexAttribs.push_back( vas );
476 materials.push_back( material );
477 } // of element iteration
481 // read a binary file and populate the provided structures.
482 bool SGBinObject::read_bin( const string& file ) {
487 sgSimpleBuffer buf( 32768 ); // 32 Kb
489 // zero out structures
490 gbs_center = SGVec3d(0, 0, 0);
502 pt_materials.clear();
509 tri_materials.clear();
516 strip_materials.clear();
523 fan_materials.clear();
526 if ( (fp = gzopen( file.c_str(), "rb" )) == NULL ) {
527 string filegz = file + ".gz";
528 if ( (fp = gzopen( filegz.c_str(), "rb" )) == NULL ) {
529 SG_LOG( SG_EVENT, SG_ALERT,
530 "ERROR: opening " << file << " or " << filegz << " for reading!");
532 throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file));
540 sgReadUInt( fp, &header );
541 if ( ((header & 0xFF000000) >> 24) == 'S' &&
542 ((header & 0x00FF0000) >> 16) == 'G' ) {
545 version = (header & 0x0000FFFF);
547 // close the file before we return
549 throw sg_io_exception("Bad BTG magic/version", sg_location(file));
552 // read creation time
553 unsigned int foo_calendar_time;
554 sgReadUInt( fp, &foo_calendar_time );
557 time_t calendar_time = foo_calendar_time;
558 // The following code has a global effect on the host application
559 // and can screws up the time elsewhere. It should be avoided
560 // unless you need this for debugging in which case you should
561 // disable it again once the debugging task is finished.
563 local_tm = localtime( &calendar_time );
565 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
566 SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str);
569 // read number of top level objects
571 if ( version >= 10) { // version 10 extends everything to be 32-bit
572 sgReadInt( fp, &nobjects );
573 } else if ( version >= 7 ) {
575 sgReadUShort( fp, &v );
579 sgReadShort( fp, &v );
583 SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
585 if ( sgReadError() ) {
586 throw sg_io_exception("Error reading BTG file header", sg_location(file));
590 for ( i = 0; i < nobjects; ++i ) {
591 // read object header
593 uint32_t nproperties, nelements;
594 sgReadChar( fp, &obj_type );
595 if ( version >= 10 ) {
596 sgReadUInt( fp, &nproperties );
597 sgReadUInt( fp, &nelements );
598 } else if ( version >= 7 ) {
600 sgReadUShort( fp, &v );
602 sgReadUShort( fp, &v );
606 sgReadShort( fp, &v );
608 sgReadShort( fp, &v );
612 SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
613 " = " << (int)obj_type << " props = " << nproperties <<
614 " elements = " << nelements);
616 if ( obj_type == SG_BOUNDING_SPHERE ) {
617 // read bounding sphere properties
618 read_properties( fp, nproperties );
620 // read bounding sphere elements
621 for ( j = 0; j < nelements; ++j ) {
622 sgReadUInt( fp, &nbytes );
623 buf.resize( nbytes );
625 char *ptr = buf.get_ptr();
626 sgReadBytes( fp, nbytes, ptr );
627 gbs_center = buf.readVec3d();
628 gbs_radius = buf.readFloat();
630 } else if ( obj_type == SG_VERTEX_LIST ) {
631 // read vertex list properties
632 read_properties( fp, nproperties );
634 // read vertex list elements
635 for ( j = 0; j < nelements; ++j ) {
636 sgReadUInt( fp, &nbytes );
637 buf.resize( nbytes );
639 char *ptr = buf.get_ptr();
640 sgReadBytes( fp, nbytes, ptr );
641 int count = nbytes / (sizeof(float) * 3);
642 wgs84_nodes.reserve( count );
643 for ( k = 0; k < count; ++k ) {
644 SGVec3f v = buf.readVec3f();
645 // extend from float to double, hmmm
646 wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
649 } else if ( obj_type == SG_COLOR_LIST ) {
650 // read color list properties
651 read_properties( fp, nproperties );
653 // read color list elements
654 for ( j = 0; j < nelements; ++j ) {
655 sgReadUInt( fp, &nbytes );
656 buf.resize( nbytes );
658 char *ptr = buf.get_ptr();
659 sgReadBytes( fp, nbytes, ptr );
660 int count = nbytes / (sizeof(float) * 4);
661 colors.reserve(count);
662 for ( k = 0; k < count; ++k ) {
663 colors.push_back( buf.readVec4f() );
666 } else if ( obj_type == SG_NORMAL_LIST ) {
667 // read normal list properties
668 read_properties( fp, nproperties );
670 // read normal list elements
671 for ( j = 0; j < nelements; ++j ) {
672 sgReadUInt( fp, &nbytes );
673 buf.resize( nbytes );
675 unsigned char *ptr = (unsigned char *)(buf.get_ptr());
676 sgReadBytes( fp, nbytes, ptr );
677 int count = nbytes / 3;
678 normals.reserve( count );
680 for ( k = 0; k < count; ++k ) {
681 SGVec3f normal( (ptr[0]) / 127.5 - 1.0,
682 (ptr[1]) / 127.5 - 1.0,
683 (ptr[2]) / 127.5 - 1.0);
684 normals.push_back(normalize(normal));
688 } else if ( obj_type == SG_TEXCOORD_LIST ) {
689 // read texcoord list properties
690 read_properties( fp, nproperties );
692 // read texcoord list elements
693 for ( j = 0; j < nelements; ++j ) {
694 sgReadUInt( fp, &nbytes );
695 buf.resize( nbytes );
697 char *ptr = buf.get_ptr();
698 sgReadBytes( fp, nbytes, ptr );
699 int count = nbytes / (sizeof(float) * 2);
700 texcoords.reserve(count);
701 for ( k = 0; k < count; ++k ) {
702 texcoords.push_back( buf.readVec2f() );
705 } else if ( obj_type == SG_VA_FLOAT_LIST ) {
706 // read vertex attribute (float) properties
707 read_properties( fp, nproperties );
709 // read vertex attribute list elements
710 for ( j = 0; j < nelements; ++j ) {
711 sgReadUInt( fp, &nbytes );
712 buf.resize( nbytes );
714 char *ptr = buf.get_ptr();
715 sgReadBytes( fp, nbytes, ptr );
716 int count = nbytes / (sizeof(float));
717 va_flt.reserve(count);
718 for ( k = 0; k < count; ++k ) {
719 va_flt.push_back( buf.readFloat() );
722 } else if ( obj_type == SG_VA_INTEGER_LIST ) {
723 // read vertex attribute (integer) properties
724 read_properties( fp, nproperties );
726 // read vertex attribute list elements
727 for ( j = 0; j < nelements; ++j ) {
728 sgReadUInt( fp, &nbytes );
729 buf.resize( nbytes );
731 char *ptr = buf.get_ptr();
732 sgReadBytes( fp, nbytes, ptr );
733 int count = nbytes / (sizeof(unsigned int));
734 va_int.reserve(count);
735 for ( k = 0; k < count; ++k ) {
736 va_int.push_back( buf.readInt() );
739 } else if ( obj_type == SG_POINTS ) {
740 // read point elements
741 read_object( fp, SG_POINTS, nproperties, nelements,
742 pts_v, pts_n, pts_c, pts_tcs,
743 pts_vas, pt_materials );
744 } else if ( obj_type == SG_TRIANGLE_FACES ) {
745 // read triangle face properties
746 read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
747 tris_v, tris_n, tris_c, tris_tcs,
748 tris_vas, tri_materials );
749 } else if ( obj_type == SG_TRIANGLE_STRIPS ) {
750 // read triangle strip properties
751 read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
752 strips_v, strips_n, strips_c, strips_tcs,
753 strips_vas, strip_materials );
754 } else if ( obj_type == SG_TRIANGLE_FANS ) {
755 // read triangle fan properties
756 read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
757 fans_v, fans_n, fans_c, fans_tcs,
758 fans_vas, fan_materials );
760 // unknown object type, just skip
761 read_properties( fp, nproperties );
764 for ( j = 0; j < nelements; ++j ) {
765 sgReadUInt( fp, &nbytes );
766 // cout << "element size = " << nbytes << endl;
767 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
768 char *ptr = buf.get_ptr();
769 sgReadBytes( fp, nbytes, ptr );
773 if ( sgReadError() ) {
774 throw sg_io_exception("Error while reading object", sg_location(file, i));
784 void SGBinObject::write_header(gzFile fp, int type, int nProps, int nElements)
786 sgWriteChar(fp, (unsigned char) type);
788 sgWriteUShort(fp, nProps);
789 sgWriteUShort(fp, nElements);
791 sgWriteUInt(fp, nProps);
792 sgWriteUInt(fp, nElements);
796 unsigned int SGBinObject::count_objects(const string_list& materials)
798 unsigned int result = 0;
799 unsigned int start = 0, end = 1;
800 unsigned int count = materials.size();
803 while ( start < count ) {
804 m = materials[start];
805 for (end = start+1; (end < count) && (m == materials[end]); ++end) { }
813 void SGBinObject::write_objects(gzFile fp, int type,
814 const group_list& verts,
815 const group_list& normals,
816 const group_list& colors,
817 const group_tci_list& texCoords,
818 const group_vai_list& vertexAttribs,
819 const string_list& materials)
825 unsigned int start = 0, end = 1;
829 while (start < materials.size()) {
830 m = materials[start];
831 // find range of objects with identical material, write out as a single object
832 for (end = start+1; (end < materials.size()) && (m == materials[end]); ++end) {}
834 // calc the number of elements
835 const int count = end - start;
837 // calc the number of properties
838 unsigned int va_mask = 0;
839 unsigned int va_count = vertexAttribs.size();
840 for ( unsigned int va=0; va<va_count; va++ ) {
841 if ( !vertexAttribs[va].empty() && !vertexAttribs[va].front().empty() ) {
842 va_mask |= ( 1 << va );
847 write_header(fp, type, 3, count);
849 write_header(fp, type, 2, count);
854 sgWriteChar( fp, (char)SG_MATERIAL ); // property
855 sgWriteUInt( fp, m.length() ); // nbytes
856 sgWriteBytes( fp, m.length(), m.c_str() );
858 // index mask property
859 unsigned char idx_mask = 0;
860 if ( !verts.empty() && !verts[start].empty()) idx_mask |= SG_IDX_VERTICES;
861 if ( !normals.empty() && !normals[start].empty()) idx_mask |= SG_IDX_NORMALS;
862 if ( !colors.empty() && !colors[start].empty()) idx_mask |= SG_IDX_COLORS;
863 if ( !texCoords.empty() && !texCoords[start][0].empty()) idx_mask |= SG_IDX_TEXCOORDS_0;
864 if ( !texCoords.empty() && !texCoords[start][1].empty()) idx_mask |= SG_IDX_TEXCOORDS_1;
865 if ( !texCoords.empty() && !texCoords[start][2].empty()) idx_mask |= SG_IDX_TEXCOORDS_2;
866 if ( !texCoords.empty() && !texCoords[start][3].empty()) idx_mask |= SG_IDX_TEXCOORDS_3;
869 SG_LOG(SG_IO, SG_ALERT, "SGBinObject::write_objects: object with material:"
870 << m << "has no indices set");
873 sgWriteChar( fp, (char)SG_INDEX_TYPES ); // property
874 sgWriteUInt( fp, 1 ); // nbytes
875 sgWriteChar( fp, idx_mask );
877 // vertex attribute property
879 sgWriteChar( fp, (char)SG_VERT_ATTRIBS ); // property
880 sgWriteUInt( fp, 4 ); // nbytes
881 sgWriteChar( fp, va_mask );
885 for (unsigned int i=start; i < end; ++i) {
886 const int_list& va(verts[i]);
887 const int_list& na((idx_mask & SG_IDX_NORMALS) ? normals[i] : emptyList);
888 const int_list& ca((idx_mask & SG_IDX_COLORS) ? colors[i] : emptyList);
890 // pass the whole texcoord array - we'll figure out which indicies to write
892 const tci_list& tca( texCoords[i] );
894 // pass the whole vertex array - we'll figure out which indicies to write
896 const vai_list& vaa( vertexAttribs[i] );
899 write_indices<uint16_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
901 write_indices<uint32_t>(fp, idx_mask, va_mask, va, na, ca, tca, vaa);
906 } // of materials iteration
909 // write out the structures to a binary file. We assume that the
910 // groups come to us sorted by material property. If not, things
911 // don't break, but the result won't be as optimal.
912 bool SGBinObject::write_bin( const string& base, const string& name,
916 SGPath file = base + "/" + b.gen_base_path() + "/" + name + ".gz";
917 return write_bin_file(file);
920 static unsigned int max_object_size( const string_list& materials )
922 unsigned int max_size = 0;
924 for (unsigned int start=0; start < materials.size();) {
925 string m = materials[start];
926 unsigned int end = start + 1;
927 // find range of objects with identical material, calc its size
928 for (; (end < materials.size()) && (m == materials[end]); ++end) {}
930 unsigned int cur_size = end - start;
931 max_size = std::max(max_size, cur_size);
938 const unsigned int VERSION_7_MATERIAL_LIMIT = 0x7fff;
940 bool SGBinObject::write_bin_file(const SGPath& file)
945 file2.create_dir( 0755 );
948 if ( (fp = gzopen( file.c_str(), "wb9" )) == NULL ) {
949 cout << "ERROR: opening " << file.str() << " for writing!" << endl;
955 SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
956 << " pt_materials = " << pt_materials.size() );
957 SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
958 << " tri_materials = " << tri_materials.size() );
959 SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
960 << " strip_materials = " << strip_materials.size() );
961 SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
962 << " fan_materials = " << fan_materials.size() );
964 SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
965 SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
966 SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
967 SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
970 bool shortMaterialsRanges =
971 (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) &&
972 (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) &&
973 (max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) &&
974 (max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT);
976 if ((wgs84_nodes.size() < 0xffff) &&
977 (normals.size() < 0xffff) &&
978 (texcoords.size() < 0xffff) &&
979 shortMaterialsRanges) {
980 version = 7; // use smaller indices if possible
983 // write header magic
985 /** Magic Number for our file format */
986 #define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version )
988 sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
989 time_t calendar_time = time(NULL);
990 sgWriteLong( fp, (int32_t)calendar_time );
992 // calculate and write number of top level objects
993 int nobjects = 5; // gbs, vertices, colors, normals, texcoords
994 nobjects += count_objects(pt_materials);
995 nobjects += count_objects(tri_materials);
996 nobjects += count_objects(strip_materials);
997 nobjects += count_objects(fan_materials);
999 SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
1002 sgWriteUShort( fp, (uint16_t) nobjects );
1004 sgWriteInt( fp, nobjects );
1007 // write bounding sphere
1008 write_header( fp, SG_BOUNDING_SPHERE, 0, 1);
1009 sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
1010 sgWritedVec3( fp, gbs_center );
1011 sgWriteFloat( fp, gbs_radius );
1014 write_header( fp, SG_VERTEX_LIST, 0, 1);
1015 sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
1016 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1017 sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center));
1020 // dump vertex color list
1021 write_header( fp, SG_COLOR_LIST, 0, 1);
1022 sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes
1023 for ( i = 0; i < (int)colors.size(); ++i ) {
1024 sgWriteVec4( fp, colors[i]);
1027 // dump vertex normal list
1028 write_header( fp, SG_NORMAL_LIST, 0, 1);
1029 sgWriteUInt( fp, normals.size() * 3 ); // nbytes
1031 for ( i = 0; i < (int)normals.size(); ++i ) {
1032 SGVec3f p = normals[i];
1033 normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
1034 normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
1035 normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
1036 sgWriteBytes( fp, 3, normal );
1039 // dump texture coordinates
1040 write_header( fp, SG_TEXCOORD_LIST, 0, 1);
1041 sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
1042 for ( i = 0; i < (int)texcoords.size(); ++i ) {
1043 sgWriteVec2( fp, texcoords[i]);
1046 write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
1047 write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
1048 write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
1049 write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
1054 if ( sgWriteError() ) {
1055 cout << "Error while writing file " << file.str() << endl;
1063 // write out the structures to an ASCII file. We assume that the
1064 // groups come to us sorted by material property. If not, things
1065 // don't break, but the result won't be as optimal.
1066 bool SGBinObject::write_ascii( const string& base, const string& name,
1071 SGPath file = base + "/" + b.gen_base_path() + "/" + name;
1072 file.create_dir( 0755 );
1073 cout << "Output file = " << file.str() << endl;
1076 if ( (fp = fopen( file.c_str(), "w" )) == NULL ) {
1077 cout << "ERROR: opening " << file.str() << " for writing!" << endl;
1081 cout << "triangles size = " << tris_v.size() << " tri_materials = "
1082 << tri_materials.size() << endl;
1083 cout << "strips size = " << strips_v.size() << " strip_materials = "
1084 << strip_materials.size() << endl;
1085 cout << "fans size = " << fans_v.size() << " fan_materials = "
1086 << fan_materials.size() << endl;
1088 cout << "points = " << wgs84_nodes.size() << endl;
1089 cout << "tex coords = " << texcoords.size() << endl;
1091 fprintf(fp, "# FGFS Scenery\n");
1092 fprintf(fp, "# Version %s\n", SG_SCENERY_FILE_FORMAT);
1094 time_t calendar_time = time(NULL);
1095 struct tm *local_tm;
1096 local_tm = localtime( &calendar_time );
1098 strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
1099 fprintf(fp, "# Created %s\n", time_str );
1102 // write bounding sphere
1103 fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n",
1104 gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius);
1108 fprintf(fp, "# vertex list\n");
1109 for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
1110 SGVec3d p = wgs84_nodes[i] - gbs_center;
1112 fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1116 fprintf(fp, "# vertex normal list\n");
1117 for ( i = 0; i < (int)normals.size(); ++i ) {
1118 SGVec3f p = normals[i];
1119 fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
1123 // dump texture coordinates
1124 fprintf(fp, "# texture coordinate list\n");
1125 for ( i = 0; i < (int)texcoords.size(); ++i ) {
1126 SGVec2f p = texcoords[i];
1127 fprintf(fp, "vt %.5f %.5f\n", p.x(), p.y() );
1131 // dump individual triangles if they exist
1132 if ( ! tris_v.empty() ) {
1133 fprintf(fp, "# triangle groups\n");
1138 while ( start < (int)tri_materials.size() ) {
1140 material = tri_materials[start];
1141 while ( (end < (int)tri_materials.size()) &&
1142 (material == tri_materials[end]) )
1144 // cout << "end = " << end << endl;
1147 // cout << "group = " << start << " to " << end - 1 << endl;
1149 SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
1150 for ( i = start; i < end; ++i ) {
1151 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1152 d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
1156 SGVec3d bs_center = d.getCenter();
1157 double bs_radius = d.getRadius();
1159 // write group headers
1161 fprintf(fp, "# usemtl %s\n", material.c_str());
1162 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1163 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1166 for ( i = start; i < end; ++i ) {
1168 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1169 fprintf(fp, " %d/%d", tris_v[i][j], tris_tcs[i][0][j] );
1179 // dump triangle groups
1180 if ( ! strips_v.empty() ) {
1181 fprintf(fp, "# triangle strips\n");
1186 while ( start < (int)strip_materials.size() ) {
1188 material = strip_materials[start];
1189 while ( (end < (int)strip_materials.size()) &&
1190 (material == strip_materials[end]) )
1192 // cout << "end = " << end << endl;
1195 // cout << "group = " << start << " to " << end - 1 << endl;
1198 SGSphered d( SGVec3d(0.0, 0.0, 0.0), -1.0 );
1199 for ( i = start; i < end; ++i ) {
1200 for ( j = 0; j < (int)tris_v[i].size(); ++j ) {
1201 d.expandBy(wgs84_nodes[ tris_v[i][j] ]);
1205 SGVec3d bs_center = d.getCenter();
1206 double bs_radius = d.getRadius();
1208 // write group headers
1210 fprintf(fp, "# usemtl %s\n", material.c_str());
1211 fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n",
1212 bs_center.x(), bs_center.y(), bs_center.z(), bs_radius);
1215 for ( i = start; i < end; ++i ) {
1217 for ( j = 0; j < (int)strips_v[i].size(); ++j ) {
1218 fprintf(fp, " %d/%d", strips_v[i][j], strips_tcs[i][0][j] );
1231 string command = "gzip --force --best " + file.str();
1232 int err = system(command.c_str());
1235 cout << "ERROR: gzip " << file.str() << " failed!" << endl;
1241 void SGBinObject::read_properties(gzFile fp, int nproperties)
1247 for ( int j = 0; j < nproperties; ++j ) {
1249 sgReadChar( fp, &prop_type );
1250 sgReadUInt( fp, &nbytes );
1251 // cout << "property size = " << nbytes << endl;
1252 if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
1253 char *ptr = buf.get_ptr();
1254 sgReadBytes( fp, nbytes, ptr );
1258 bool SGBinObject::add_point( const SGBinObjectPoint& pt )
1260 // add the point info
1261 pt_materials.push_back( pt.material );
1263 pts_v.push_back( pt.v_list );
1264 pts_n.push_back( pt.n_list );
1265 pts_c.push_back( pt.c_list );
1270 bool SGBinObject::add_triangle( const SGBinObjectTriangle& tri )
1272 // add the triangle info and keep lists aligned
1273 tri_materials.push_back( tri.material );
1274 tris_v.push_back( tri.v_list );
1275 tris_n.push_back( tri.n_list );
1276 tris_c.push_back( tri.c_list );
1277 tris_tcs.push_back( tri.tc_list );
1278 tris_vas.push_back( tri.va_list );