diff --git a/src/osgPlugins/OpenFlight/AttrData.cpp b/src/osgPlugins/OpenFlight/AttrData.cpp index 8d32a3df7..2bb1d18d2 100644 --- a/src/osgPlugins/OpenFlight/AttrData.cpp +++ b/src/osgPlugins/OpenFlight/AttrData.cpp @@ -24,7 +24,7 @@ using namespace flt; AttrData::AttrData() : texels_u(0), - textel_v(0), + texels_v(0), direction_u(0), direction_v(0), x_up(0), diff --git a/src/osgPlugins/OpenFlight/AttrData.h b/src/osgPlugins/OpenFlight/AttrData.h index b591a1961..06b457f13 100644 --- a/src/osgPlugins/OpenFlight/AttrData.h +++ b/src/osgPlugins/OpenFlight/AttrData.h @@ -22,7 +22,7 @@ #include #include -#include "types.h" +#include "Types.h" namespace flt { @@ -112,7 +112,7 @@ class AttrData : public osg::Object }; int32 texels_u; // Number of texels in u direction - int32 textel_v; // Number of texels in v direction + int32 texels_v; // Number of texels in v direction int32 direction_u; // Real world size u direction int32 direction_v; // Real world size v direction int32 x_up; // x component of up vector @@ -328,6 +328,7 @@ class AttrData : public osg::Object // definition measured in texels. } #endif + int32 numSubtextures; // # of subtextures }; diff --git a/src/osgPlugins/OpenFlight/CMakeLists.txt b/src/osgPlugins/OpenFlight/CMakeLists.txt index cf529e246..c29293fce 100644 --- a/src/osgPlugins/OpenFlight/CMakeLists.txt +++ b/src/osgPlugins/OpenFlight/CMakeLists.txt @@ -6,9 +6,18 @@ SET(TARGET_SRC AttrData.cpp ControlRecords.cpp DataInputStream.cpp + DataOutputStream.cpp Document.cpp + expAncillaryRecords.cpp + expControlRecords.cpp + expGeometryRecords.cpp + expPrimaryRecords.cpp + ExportOptions.cpp + FltExportVisitor.cpp GeometryRecords.cpp LightPointRecords.cpp + LightSourcePaletteManager.cpp + MaterialPaletteManager.cpp PaletteRecords.cpp Pools.cpp PrimaryRecords.cpp @@ -19,21 +28,32 @@ SET(TARGET_SRC Registry.cpp ReservedRecords.cpp RoadRecords.cpp + TexturePaletteManager.cpp Vertex.cpp + VertexPaletteManager.cpp VertexRecords.cpp ) SET(TARGET_H AttrData.h DataInputStream.h + DataOutputStream.h + ExportOptions.h + FltExportVisitor.h + FltWriteResult.h Document.h + LightSourcePaletteManager.h + MaterialPaletteManager.h Pools.h Record.h RecordInputStream.h Registry.h + TexturePaletteManager.h Vertex.h - opcodes.h - types.h + VertexPaletteManager.h + Opcodes.h + Types.h + Utils.h ) SET(TARGET_ADDED_LIBRARIES osgSim ) diff --git a/src/osgPlugins/OpenFlight/DataInputStream.h b/src/osgPlugins/OpenFlight/DataInputStream.h index ca4fa98c6..0d284947f 100644 --- a/src/osgPlugins/OpenFlight/DataInputStream.h +++ b/src/osgPlugins/OpenFlight/DataInputStream.h @@ -25,7 +25,7 @@ #include #include #include -#include "types.h" +#include "Types.h" namespace flt { diff --git a/src/osgPlugins/OpenFlight/DataOutputStream.cpp b/src/osgPlugins/OpenFlight/DataOutputStream.cpp new file mode 100644 index 000000000..d59759e0b --- /dev/null +++ b/src/osgPlugins/OpenFlight/DataOutputStream.cpp @@ -0,0 +1,202 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "DataOutputStream.h" + +#include +#include + + +using namespace flt; + + +char DataOutputStream::_null( 0 ); + + +DataOutputStream::DataOutputStream( std::streambuf* sb, bool validate ) + : std::ostream( sb ), + _validate( validate ) +{ + _byteswap = osg::getCpuByteOrder() == osg::LittleEndian; +} + + +void +DataOutputStream::writeInt8( const int8 val ) +{ + vwrite( (char*)&val, sizeof( int8 ) ); +} + +void +DataOutputStream::writeUInt8( const uint8 val ) +{ + vwrite( (char*)&val, sizeof( uint8 ) ); +} + + +void +DataOutputStream::writeInt16( const int16 val ) +{ + int16 data=val; + if (_byteswap && good()) + osg::swapBytes2( (char*)&data ); + vwrite( (char*)&data, sizeof( int16 ) ); +} + +void +DataOutputStream::writeUInt16( const uint16 val ) +{ + uint16 data=val; + if (_byteswap && good()) + osg::swapBytes2( (char*)&data ); + vwrite( (char*)&data, sizeof( uint16 ) ); +} + + +void +DataOutputStream::writeInt32( const int32 val ) +{ + int32 data=val; + if (_byteswap && good()) + osg::swapBytes4( (char*)&data ); + vwrite( (char*)&data, sizeof( int32 ) ); +} + +void +DataOutputStream::writeUInt32( const uint32 val ) +{ + uint32 data=val; + if (_byteswap && good()) + osg::swapBytes4( (char*)&data ); + vwrite( (char*)&data, sizeof( uint32 ) ); +} + + +void +DataOutputStream::writeFloat32( const float32 val ) +{ + float32 data=val; + if (_byteswap && good()) + osg::swapBytes4( (char*)&data ); + vwrite( (char*)&data, sizeof( float32 ) ); +} + +void +DataOutputStream::writeFloat64( const float64 val ) +{ + float64 data=val; + if (_byteswap && good()) + osg::swapBytes8( (char*)&data ); + vwrite( (char*)&data, sizeof( float64 ) ); +} + + +void +DataOutputStream::writeString( const std::string& val, bool nullTerminate ) +{ + vwrite( const_cast( val.c_str() ), val.size() ); + if (nullTerminate) + vwrite( &_null, 1 ); +} + +void +DataOutputStream::writeString( const std::string& val, int size, char fill ) +{ + if (val.size() > ((unsigned int)size)-1) + { + vwrite( const_cast( val.c_str() ), size-1 ); + vwrite( &fill, 1 ); + } + else + { + vwrite( const_cast( val.c_str() ), val.size() ); + writeFill( size - val.size(), fill ); + } +} + +void +DataOutputStream::writeID( const std::string& val ) +{ + unsigned int len = val.size(); + + vwrite( const_cast( val.c_str() ), len ); + + while (len++ < 8) + vwrite( &_null, 1 ); +} + + +void +DataOutputStream::writeColor32( const osg::Vec4f& val ) +{ + writeUInt8( val.a() ); + writeUInt8( val.b() ); + writeUInt8( val.g() ); + writeUInt8( val.r() ); +} + +void +DataOutputStream::writeVec2f( const osg::Vec2f& val ) +{ + writeFloat32( val.x() ); + writeFloat32( val.y() ); +} + +void +DataOutputStream::writeVec3f( const osg::Vec3f& val ) +{ + writeFloat32( val.x() ); + writeFloat32( val.y() ); + writeFloat32( val.z() ); +} + +void +DataOutputStream::writeVec4f( const osg::Vec4f& val ) +{ + writeFloat32( val.x() ); + writeFloat32( val.y() ); + writeFloat32( val.z() ); + writeFloat32( val.w() ); +} + +void +DataOutputStream::writeVec3d( const osg::Vec3d& val ) +{ + writeFloat64( val.x() ); + writeFloat64( val.y() ); + writeFloat64( val.z() ); +} + + +void +DataOutputStream::writeFill( int sizeBytes, const char val ) +{ + for (int i = 0; i < sizeBytes; ++i) + { + put(val); + } +} + + +std::ostream& +DataOutputStream::vwrite( char_type* str, std::streamsize count ) +{ + if (_validate) + return *this; + else + return write( str, count ); +} + diff --git a/src/osgPlugins/OpenFlight/DataOutputStream.h b/src/osgPlugins/OpenFlight/DataOutputStream.h new file mode 100644 index 000000000..d9793cc24 --- /dev/null +++ b/src/osgPlugins/OpenFlight/DataOutputStream.h @@ -0,0 +1,78 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_DATA_OUTPUT_STREAM_H__ +#define __FLTEXP_DATA_OUTPUT_STREAM_H__ 1 + + +#include +#include +#include +#include +#include +#include "Types.h" +//#include "Export.h" + + +namespace flt { + + +class Record; + +class DataOutputStream : public std::ostream +{ +public: + explicit DataOutputStream( std::streambuf* sb, bool validate=false ); + + void writeInt8( const int8 val ); + void writeUInt8( const uint8 val ); + void writeInt16( const int16 val ); + void writeUInt16( const uint16 val ); + void writeInt32( const int32 val ); + void writeUInt32( const uint32 val ); + void writeFloat32( const float32 val ); + void writeFloat64( const float64 val ); + + // Write the entire string. If nullTerminate is true, write an additional NULL. + // Always writes either 'val.size()' bytes or 'val.size()+1' bytes. + void writeString( const std::string& val, bool nullTerminate=true ); + + // Never write more than 'size-1' bytes from 'val', and write 'fill' so that 'size' bytes total are written. + // Always writes 'size' bytes.. + void writeString( const std::string& val, int size, char fill='\0' ); + + void writeID( const std::string& val ); + void writeColor32( const osg::Vec4f& val ); + void writeVec2f( const osg::Vec2f& val ); + void writeVec3f( const osg::Vec3f& val ); + void writeVec4f( const osg::Vec4f& val ); + void writeVec3d( const osg::Vec3d& val ); + + void writeFill( int sizeBytes, const char val='\0' ); + +protected: + virtual std::ostream& vwrite( char_type* str, std::streamsize count ); + + bool _byteswap; + bool _validate; + + static char _null; +}; + +} + +#endif + diff --git a/src/osgPlugins/OpenFlight/Document.h b/src/osgPlugins/OpenFlight/Document.h index 748a8b2d4..38194fe71 100644 --- a/src/osgPlugins/OpenFlight/Document.h +++ b/src/osgPlugins/OpenFlight/Document.h @@ -26,7 +26,7 @@ #include #include -#include "types.h" +#include "Types.h" #include "Record.h" #include "Pools.h" diff --git a/src/osgPlugins/OpenFlight/ExportOptions.cpp b/src/osgPlugins/OpenFlight/ExportOptions.cpp new file mode 100644 index 000000000..265120774 --- /dev/null +++ b/src/osgPlugins/OpenFlight/ExportOptions.cpp @@ -0,0 +1,231 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include +#include + +#include +#include +#include +#include + +#include "ExportOptions.h" +//#include "DataOutputStream.h" +//#include "FltNodeVisitor.h" + +#include +#include + +namespace flt +{ + + + +/** @name Valid Option Strings + * This plugin supports the following \c Option string values. + */ +//@{ +/** Value: "version". + * Specifies the version of the output OpenFlight file. Supported values + * include 15.7, 15.8, and 16.1. Default is 16.1. Example: + * "version=15.8". + */ +std::string ExportOptions::_versionOption( "version" ); +/** Value: "units". + * Specifies the contents of the \c Units field of the OpenFliht header record. + * Valid values include INCHES, FEET, METERS, KILOMETERS, and NATICAL_MILES. + * Default is METERS. Example: "units=METERS". + */ +std::string ExportOptions::_unitsOption( "units" ); +/** Value: "validate". + * If present in the Options string, the plugin does not write an OpenFlight file. + * Instead, it returns an indication of the scene graph's suitability for + * OpenFlight export. + */ +std::string ExportOptions::_validateOption( "validate" ); +/** Value: "tempDir". + * Specifies the directory to use for creation of temporary files. If not + * specified, the directory is taken from the file name. If the file doesn't + * contain a path, the current working directory is used. Applications should + * set this to the name of their app-specific temp directory. If the path + * contains spaces, use double quotes to ensure correct parsing. Examples: + * "tempDir=/tmp". + * "tempDir=\"C:\\My Temp Dir\"". + */ +std::string ExportOptions::_tempDirOption( "tempDir" ); +/** Value: "lighting". + * Specifies a default enable/disable state for lighting, for Nodes in the + * exported scene graph that don't set it explicitly. By default, the + * exporter assumes lighting is enabled (GL_LIGHTING ON). Set this to + * either ON or OFF. Example: + * "lighting=OFF". + */ +std::string ExportOptions::_lightingOption( "lighting" ); +/** Value: "stripTextureFilePath". + * If present in the Options string, the exporter strips the path from + * texture file names, and writes only the texure file name to the FLT + * Texture Palette. By default, the exporter doesn't strip the path, + * and the name written to the Texture Palette is taken directly from + * the osg::Image object referenced by the osg::Texture2D StateAttribute. + */ +std::string ExportOptions::_stripTextureFilePathOption( "stripTextureFilePath" ); +//@} + + +using namespace osgDB; + + +const int ExportOptions::VERSION_15_7( 1570 ); +const int ExportOptions::VERSION_15_8( 1580 ); +const int ExportOptions::VERSION_16_1( 1610 ); + + + +ExportOptions::ExportOptions() + : _version( VERSION_16_1 ), + _units( METERS ), + _validate( false ), + _lightingDefault( true ), + _stripTextureFilePath( false ) +{ +} + +ExportOptions::ExportOptions( const osgDB::ReaderWriter::Options* opt ) + : _version( VERSION_16_1 ), + _units( METERS ), + _validate( false ), + _lightingDefault( true ) +{ + if (opt) + { + const ExportOptions* fltOpt = dynamic_cast( opt ); + if (fltOpt) + { + _version = fltOpt->_version; + _units = fltOpt->_units; + _validate = fltOpt->_validate; + _tempDir = fltOpt->_tempDir; + _lightingDefault = fltOpt->_lightingDefault; + } + setOptionString( opt->getOptionString() ); + } +} + +void +ExportOptions::parseOptionsString() +{ + // Parse out the option string and store values directly in + // ExportOptions member variables. + + const std::string& str = getOptionString(); + if (str.empty()) + return; + + std::string::size_type pos( 0 ); + while (pos != str.npos) + { + // Skip leading spaces. + while ( (pos < str.length()) && + (str[pos] == ' ') ) + pos++; + + // Get the next token + std::string::size_type count = str.substr( pos ).find_first_of( " =" ); + std::string token = str.substr( pos, count ); + if (count == str.npos) + pos = str.npos; + else + pos += (count+1); + + // See if it's a Boolen/toggle + if ( token == _validateOption ) + { + osg::notify( osg::INFO ) << "fltexp: Found: " << token << std::endl; + setValidateOnly( true ); + continue; + } + if ( token == _stripTextureFilePathOption ) + { + osg::notify( osg::INFO ) << "fltexp: Found: " << token << std::endl; + setStripTextureFilePath( true ); + continue; + } + + // Not a Boolean/toggle. Must have a value. + // Get the value of the token, which could be double-quoted. + if( str[pos] == '"' ) + { + ++pos; + count = str.substr( pos ).find_first_of( '"' ); + } + else + count = str.substr( pos ).find_first_of( ' ' ); + std::string value = str.substr( pos, count ); + if (count == str.npos) + pos = str.npos; + else + pos += (count+1); + + if (token == _versionOption) + { + osg::notify( osg::INFO ) << "fltexp: Token: " << token << ", Value: " << value << std::endl; + int version( VERSION_16_1 ); + if( value == std::string( "15.7" ) ) + version = VERSION_15_7; + else if( value == std::string( "15.8" ) ) + version = VERSION_15_8; + else if( value != std::string( "16.1" ) ) + osg::notify( osg::WARN ) << "fltexp: Unsupported version: " << value << ". Defaulting to 16.1." << std::endl; + setFlightFileVersionNumber( version ); + } + else if (token == _unitsOption) + { + osg::notify( osg::INFO ) << "fltexp: Token: " << token << ", Value: " << value << std::endl; + FlightUnits units( METERS ); + if( value == std::string( "KILOMETERS" ) ) + units = KILOMETERS; + else if( value == std::string( "FEET" ) ) + units = FEET; + else if( value == std::string( "INCHES" ) ) + units = INCHES; + else if( value == std::string( "NAUTICAL_MILES" ) ) + units = NAUTICAL_MILES; + else if( value != std::string( "METERS" ) ) + osg::notify( osg::WARN ) << "fltexp: Unsupported units: " << value << ". Defaulting to METERS." << std::endl; + setFlightUnits( units ); + } + else if (token == _tempDirOption) + { + osg::notify( osg::INFO ) << "fltexp: Token: " << token << ", Value: " << value << std::endl; + setTempDir( value ); + } + else if (token == _lightingOption) + { + osg::notify( osg::INFO ) << "fltexp: Token: " << token << ", Value: " << value << std::endl; + bool lighting( true ); + if (value == std::string( "OFF" ) ) + lighting = false; + else if (value != std::string( "ON" ) ) + osg::notify( osg::WARN ) << "fltexp: Unsupported lighting value: " << value << ". Defaulting to ON." << std::endl; + setLightingDefault( lighting ); + } + else + osg::notify( osg::WARN ) << "fltexp: Bogus OptionString: " << token << std::endl; + } +} + + +} diff --git a/src/osgPlugins/OpenFlight/ExportOptions.h b/src/osgPlugins/OpenFlight/ExportOptions.h new file mode 100644 index 000000000..b1907bef3 --- /dev/null +++ b/src/osgPlugins/OpenFlight/ExportOptions.h @@ -0,0 +1,104 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLT_EXPORT_OPTIONS_H__ +#define __FLT_EXPORT_OPTIONS_H__ 1 + +#include "FltWriteResult.h" + +#include +#include +#include +#include + +#include +#include +#include + + +namespace flt +{ + + +/*! + Options class for controlling export behavior. + Features a parser for the Option string as well as getter + methods for supported options. + */ +class ExportOptions : public osgDB::ReaderWriter::Options +{ +public: + ExportOptions( const Options* opt ); + ExportOptions(); + + static const int VERSION_15_7; + static const int VERSION_15_8; + static const int VERSION_16_1; + + enum FlightUnits + { + METERS, + KILOMETERS, + FEET, + INCHES, + NAUTICAL_MILES + }; + + void setFlightFileVersionNumber( int num ) { _version = num; } + int getFlightFileVersionNumber() const { return _version; } + + void setFlightUnits( FlightUnits units ) { _units = units; } + FlightUnits getFlightUnits() const { return _units; } + + void setValidateOnly( bool validate ) { _validate = validate; } + bool getValidateOnly() const { return _validate; } + + void setTempDir( const std::string& dir ) { _tempDir = dir; } + std::string getTempDir() const { return _tempDir; } + + void setLightingDefault( bool lighting ) { _lightingDefault = lighting; } + bool getLightingDefault() const { return _lightingDefault; } + + void setStripTextureFilePath( bool strip ) { _stripTextureFilePath = strip; } + bool getStripTextureFilePath() const { return _stripTextureFilePath; } + + FltWriteResult & getWriteResult() const { return( wr_ ); } + + // Parse the OptionString and override values based on + // what was set in the OptionString. + void parseOptionsString(); + +protected: + int _version; + FlightUnits _units; + bool _validate; + std::string _tempDir; + bool _lightingDefault; + bool _stripTextureFilePath; + + mutable FltWriteResult wr_; + + static std::string _versionOption; + static std::string _unitsOption; + static std::string _validateOption; + static std::string _tempDirOption; + static std::string _lightingOption; + static std::string _stripTextureFilePathOption; +}; + +} + +#endif /* __OPEN_FLIGHT_WRITER_H__ */ diff --git a/src/osgPlugins/OpenFlight/FltExportVisitor.cpp b/src/osgPlugins/OpenFlight/FltExportVisitor.cpp new file mode 100644 index 000000000..fc7f1a23b --- /dev/null +++ b/src/osgPlugins/OpenFlight/FltExportVisitor.cpp @@ -0,0 +1,632 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "FltExportVisitor.h" +#include "ExportOptions.h" +#include "FltWriteResult.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include "LightSourcePaletteManager.h" +#include "MaterialPaletteManager.h" +#include "TexturePaletteManager.h" +#include "VertexPaletteManager.h" +#include "AttrData.h" +#include "Utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +// Disable this warning. It's OK for us to use 'this' in initializer list, +// as the texturePaletteManager merely stores a ref to it. +#pragma warning( disable : 4355 ) +#endif + +namespace flt +{ + + +FltExportVisitor::FltExportVisitor( DataOutputStream* dos, + ExportOptions* fltOpt ) + + : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ), + _dos( *dos ), + _fltOpt( fltOpt ), + _materialPalette( new MaterialPaletteManager( *fltOpt ) ), + _texturePalette( new TexturePaletteManager( *this, *fltOpt ) ), + _lightSourcePalette( new LightSourcePaletteManager( *fltOpt ) ), + _vertexPalette( new VertexPaletteManager( *fltOpt ) ), + _firstNode( true ) +{ + // Init the StateSet stack. + osg::StateSet* ss = new osg::StateSet; + + int unit; + for(unit=0; unit<8; unit++) + { + osg::TexEnv* texenv = new osg::TexEnv; + ss->setTextureAttributeAndModes( unit, texenv, osg::StateAttribute::OFF ); + // TBD other texture state? + } + + osg::Material* material = new osg::Material; + ss->setAttribute( material, osg::StateAttribute::OFF ); + if (fltOpt->getLightingDefault()) + ss->setMode( GL_LIGHTING, osg::StateAttribute::ON ); + else + ss->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); + + osg::CullFace* cf = new osg::CullFace; + ss->setAttributeAndModes( cf, osg::StateAttribute::OFF ); + + osg::BlendFunc* bf = new osg::BlendFunc; + ss->setAttributeAndModes( bf, osg::StateAttribute::OFF ); + + osg::PolygonOffset* po = new osg::PolygonOffset; + ss->setAttributeAndModes( po, osg::StateAttribute::OFF ); + + _stateSetStack.push_back( ss ); + + + // Temp file for storing records. Need a temp file because we candon't + // write header and palette until FltExportVisitor completes traversal. + _recordsTempName = fltOpt->getTempDir() + "/ofw_temp_records"; + _recordsStr.open( _recordsTempName.c_str(), std::ios::out | std::ios::binary ); + _records = new DataOutputStream( _recordsStr.rdbuf(), fltOpt->getValidateOnly() ); + + // Always write initial push level + writePush(); +} + +FltExportVisitor::~FltExportVisitor() +{ + // Delete our temp file. + if (_recordsStr.is_open()) + { + osg::notify( osg::WARN ) << "fltexp: FltExportVisitor destructor has an open temp file." << std::endl; + // This should not happen. FltExportVisitor::complete should close + // this file before we get to this destructor. + return; + } + osg::notify( osg::INFO ) << "fltexp: Deleting temp file " << _recordsTempName << std::endl; + FLTEXP_DELETEFILE( _recordsTempName.c_str() ); +} + + +void +FltExportVisitor::apply( osg::Group& node ) +{ + ScopedStatePushPop guard( this, node.getStateSet() ); + + if (_firstNode) + { + // On input, a FLT header creates a Group node. + // On export, we always write a Header record, but then the first Node + // we export is the Group that was created from the original input Header. + // On successive roundtrips, this results in increased redundant top-level Group nodes/records. + // Avoid this by NOT outputting anything for a top-level Group node. + _firstNode = false; + traverse( node ); + return; + } + + // A Group node could indicate one of many possible records. + // Header record -- Don't need to support this here. We always output a header. + // Group record -- HIGH + // Child of an LOD node -- HIGH Curently write out a Group record regardless. + // InstanceDefinition/InstanceReference -- MED -- multiparented Group is an instance + // Extension record -- MED + // Object record -- MED + // LightPointSystem record (if psgSim::MultiSwitch) -- LOW + + osgSim::MultiSwitch* multiSwitch = dynamic_cast( &node ); + if (multiSwitch) + { + writeSwitch( multiSwitch ); + } + + else + { + osgSim::ObjectRecordData* ord = + dynamic_cast< osgSim::ObjectRecordData* >( node.getUserData() ); + if (ord) + { + // This Group should write an Object Record. + writeObject( node, ord ); + } + else + { + // Handle other cases here. + // For now, just output a Group record. + writeGroup( node ); + } + } + + writeMatrix( node.getUserData() ); + writeComment( node ); + writePushTraverseWritePop( node ); +} + +void +FltExportVisitor::apply( osg::Sequence& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + writeSequence( node ); + writeMatrix( node.getUserData() ); + writeComment( node ); + writePushTraverseWritePop( node ); +} + +void +FltExportVisitor::apply( osg::Switch& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + writeSwitch( &node ); + + writeMatrix( node.getUserData() ); + writeComment( node ); + writePushTraverseWritePop( node ); +} + +void +FltExportVisitor::apply( osg::LOD& lodNode ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, lodNode.getStateSet() ); + + // LOD center - same for all chilrden + osg::Vec3d center = lodNode.getCenter(); + + // Iterate children of the LOD and write a separate LOD record for each, + // with that child's individual switchIn and switchOut properties + for ( size_t i = 0; i < lodNode.getNumChildren(); ++i ) + { + osg::Node* lodChild = lodNode.getChild(i); + + // Switch-in/switch-out distances may vary per child + double switchInDist = lodNode.getMaxRange(i); + double switchOutDist = lodNode.getMinRange(i); + + writeLevelOfDetail( lodNode, center, switchInDist, switchOutDist); + writeMatrix( lodNode.getUserData() ); + writeComment( lodNode ); + + // Traverse each child of the LOD + writePushTraverseWritePop( *lodChild ); + } + +} + +void +FltExportVisitor::apply( osg::MatrixTransform& node ) +{ + // Importer reads a Matrix record and inserts a MatrixTransform above + // the corrent node. We need to do the opposite: Write a Matrix record + // as an ancillary record for each child. We do that by storing the + // MatrixTransform in each child's UserData. Each child then checks + // UserData and writes a Matrix record if UserData is a MatrixTransform. + + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + osg::ref_ptr< osg::RefMatrix > m = new osg::RefMatrix; + m->set( node.getMatrix() ); + if (node.getUserData()) + { + const osg::RefMatrix* rm = dynamic_cast( node.getUserData() ); + if (rm) + (*m) *= *rm; + } + + std::vector< osg::Referenced* > saveUserDataList; + unsigned int idx; + for( idx=0; idxgetUserData() ); + node.getChild( idx )->setUserData( m.get() ); + } + + traverse( (osg::Node&)node ); + + // Restore saved UserData. + unsigned int nd = node.getNumChildren(); + while (nd--) + node.getChild( nd )->setUserData( + saveUserDataList[ nd ] ); + +} + +void +FltExportVisitor::apply( osg::PositionAttitudeTransform& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + osg::ref_ptr m = new osg::RefMatrix; + + const osg::Vec3d& trans = node.getPosition(); + const osg::Quat& rot = node.getAttitude(); + m->set(osg::Matrix::translate(trans) * osg::Matrix::rotate(rot) ); + + std::vector< osg::Referenced* > saveUserDataList; + unsigned int idx; + for( idx=0; idxgetUserData() ); + node.getChild( idx )->setUserData( m.get() ); + } + + traverse( (osg::Node&)node ); + + // Restore saved UserData. + unsigned int nd = node.getNumChildren(); + while (nd--) + node.getChild( nd )->setUserData( + saveUserDataList[ nd ] ); + +} + + +void +FltExportVisitor::apply( osg::Transform& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + osgSim::DOFTransform* dof = dynamic_cast( &node ); + + if (dof) + { + writeDegreeOfFreedom( dof); + } + + writeMatrix( node.getUserData() ); + writeComment( node ); + writePushTraverseWritePop( node ); +} + +void +FltExportVisitor::apply( osg::LightSource& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + writeLightSource( node ); + writeMatrix( node.getUserData() ); + writeComment( node ); + writePushTraverseWritePop( node ); +} + +void +FltExportVisitor::apply( osg::Geode& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + unsigned int idx; + for (idx=0; idxasGeometry(); + if (!geom) + continue; + + ScopedStatePushPop drawableGuard( this, geom->getStateSet() ); + + // Write vertex array data out to vertex palette manager + if (!isAllMesh( *geom )) + // If at least one record will be a Face record, then we + // need to write to the vertex pool. + _vertexPalette->add( *geom ); + + unsigned int jdx; + for (jdx=0; jdx < geom->getNumPrimitiveSets(); jdx++) + { + osg::PrimitiveSet* prim = geom->getPrimitiveSet( jdx ); + if (prim->getType() == osg::PrimitiveSet::DrawArraysPrimitiveType) + handleDrawArrays( dynamic_cast( prim ), *geom, node ); + else if (prim->getType() == osg::PrimitiveSet::DrawArrayLengthsPrimitiveType) + handleDrawArrayLengths( dynamic_cast( prim ), *geom, node ); + else if ( (prim->getType() == osg::PrimitiveSet::DrawElementsUBytePrimitiveType) || + (prim->getType() == osg::PrimitiveSet::DrawElementsUShortPrimitiveType) || + (prim->getType() == osg::PrimitiveSet::DrawElementsUIntPrimitiveType) ) + handleDrawElements( dynamic_cast( prim ), *geom, node ); + else + { + std::string warning( "fltexp: Unknown PrimitiveSet type." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } + } + } + + // Would traverse here if this node could have children. + // traverse( (osg::Node&)node ); + +} + +void +FltExportVisitor::apply( osg::Billboard& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + // TBD -- Not yet implemented, but HIGH priority. + // Face record -- HIGH + // Mesh record -- HIGH + + writeMatrix( node.getUserData() ); + writeComment( node ); + writePushTraverseWritePop( node ); +} + +void +FltExportVisitor::apply( osg::Node& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + osgSim::LightPointNode* lpn = dynamic_cast< osgSim::LightPointNode* >( &node ); + if (lpn) + { + writeLightPoint( lpn ); + } + else + { + // Unknown Node. Warn and return. + // (Note, if the base class of this Node was a Grouo, then apply(Group&) + // would export a Group record then continue traversal. Because we are + // a Node, there's no way to continue traversal, so just return.) + std::string warning( "fltexp: Unknown Node in OpenFlight export." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } +} + +void +FltExportVisitor::apply( osg::ProxyNode& node ) +{ + _firstNode = false; + ScopedStatePushPop guard( this, node.getStateSet() ); + + writeExternalReference( node ); + writeMatrix( node.getUserData() ); + writeComment( node ); +} + + + + + +bool +FltExportVisitor::complete( const osg::Node& node ) +{ + // Always write final pop level + writePop(); + // Done writing records, close the record data temp file. + _recordsStr.close(); + + // Write OpenFlight file front matter: heqader, vertex palette, etc. + writeHeader( node.getName() ); + + writeColorPalette(); + _materialPalette->write( _dos ); + _texturePalette->write( _dos ); + _lightSourcePalette->write( _dos ); + _vertexPalette->write( _dos ); + + // Write Comment ancillary record and specify the _dos DataOutputStream. + writeComment( node, &_dos ); + + // Copy record data temp file into final OpenFlight file. + // Yee-uck. TBD need better stream copy routine. + char buf; + std::ifstream recIn; + recIn.open( _recordsTempName.c_str(), std::ios::in | std::ios::binary ); + while (!recIn.eof() ) + { + recIn.read( &buf, 1 ); + if (recIn.good()) + _dos << buf; + } + recIn.close(); + + return true; +} + + + +// +// StateSet stack support + +void +FltExportVisitor::pushStateSet( const osg::StateSet* rhs ) +{ + osg::StateSet* ss = new osg::StateSet( *( _stateSetStack.back().get() ) ); + + if (rhs) + ss->merge( *rhs ); + + _stateSetStack.push_back( ss ); +} + +void +FltExportVisitor::popStateSet() +{ + _stateSetStack.pop_back(); +} + +const osg::StateSet* +FltExportVisitor::getCurrentStateSet() const +{ + return _stateSetStack.back().get(); +} + +void +FltExportVisitor::clearStateSetStack() +{ + _stateSetStack.clear(); +} + + +void +FltExportVisitor::writeATTRFile( int unit, const osg::Texture2D* texture ) const +{ + std::string name; + if (_fltOpt->getStripTextureFilePath()) + name = osgDB::getSimpleFileName( texture->getImage()->getFileName() ); + else + name = texture->getImage()->getFileName(); + name += std::string( ".attr" ); + + if ( osgDB::findDataFile( name ).empty() ) + { + // No .attr file found. We should write one out. + // Fill AttrData fields from current state. + AttrData ad; + + ad.texels_u = texture->getImage()->s(); + ad.texels_v = texture->getImage()->t(); + + switch( texture->getFilter( osg::Texture::MIN_FILTER ) ) + { + case osg::Texture::LINEAR: + ad.minFilterMode = AttrData::MIN_FILTER_BILINEAR; + break; + case osg::Texture::LINEAR_MIPMAP_LINEAR: + ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_TRILINEAR; + break; + case osg::Texture::LINEAR_MIPMAP_NEAREST: + ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_BILINEAR; + break; + case osg::Texture::NEAREST: + ad.minFilterMode = AttrData::MIN_FILTER_POINT; + break; + case osg::Texture::NEAREST_MIPMAP_LINEAR: + ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_LINEAR; + break; + case osg::Texture::NEAREST_MIPMAP_NEAREST: + ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_POINT; + break; + default: + ad.minFilterMode = AttrData::MIN_FILTER_MIPMAP_TRILINEAR; + break; + } + switch( texture->getFilter( osg::Texture::MAG_FILTER ) ) + { + case osg::Texture::NEAREST: + ad.magFilterMode = AttrData::MAG_FILTER_POINT; + break; + default: + ad.magFilterMode = AttrData::MAG_FILTER_BILINEAR; + break; + } + + // Repeat and Clamp + switch( texture->getWrap( osg::Texture::WRAP_S ) ) + { + case osg::Texture::CLAMP: + case osg::Texture::CLAMP_TO_EDGE: + case osg::Texture::CLAMP_TO_BORDER: + ad.wrapMode_u = AttrData::WRAP_CLAMP; + break; + case osg::Texture::REPEAT: + default: + ad.wrapMode_u = AttrData::WRAP_REPEAT; + break; + case osg::Texture::MIRROR: + if (_fltOpt->getFlightFileVersionNumber() >= 1610) + ad.wrapMode_u = AttrData::WRAP_MIRRORED_REPEAT; + else + ad.wrapMode_u = AttrData::WRAP_REPEAT; + break; + } + switch( texture->getWrap( osg::Texture::WRAP_T ) ) + { + case osg::Texture::CLAMP: + case osg::Texture::CLAMP_TO_EDGE: + case osg::Texture::CLAMP_TO_BORDER: + ad.wrapMode_v = AttrData::WRAP_CLAMP; + break; + case osg::Texture::REPEAT: + default: + ad.wrapMode_v = AttrData::WRAP_REPEAT; + break; + case osg::Texture::MIRROR: + if (_fltOpt->getFlightFileVersionNumber() >= 1610) + ad.wrapMode_v = AttrData::WRAP_MIRRORED_REPEAT; + else + ad.wrapMode_v = AttrData::WRAP_REPEAT; + break; + } + + const osg::StateSet* ss = getCurrentStateSet(); + const osg::TexEnv* texenv = dynamic_cast( + ss->getTextureAttribute( unit, osg::StateAttribute::TEXENV ) ); + switch( texenv->getMode()) + { + case osg::TexEnv::DECAL: + ad.texEnvMode = AttrData::TEXENV_DECAL; + break; + case osg::TexEnv::MODULATE: + default: + ad.texEnvMode = AttrData::TEXENV_MODULATE; + break; + case osg::TexEnv::BLEND: + ad.texEnvMode = AttrData::TEXENV_BLEND; + break; + case osg::TexEnv::REPLACE: + ad.texEnvMode = AttrData::TEXENV_COLOR; + break; + case osg::TexEnv::ADD: + ad.texEnvMode = AttrData::TEXENV_ADD; + break; + } + + osgDB::writeObjectFile( ad, name, _fltOpt.get() ); + } +} + + +} + diff --git a/src/osgPlugins/OpenFlight/FltExportVisitor.h b/src/osgPlugins/OpenFlight/FltExportVisitor.h new file mode 100644 index 000000000..120d87e80 --- /dev/null +++ b/src/osgPlugins/OpenFlight/FltExportVisitor.h @@ -0,0 +1,261 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_FLT_EXPORT_VISITOR_H__ +#define __FLTEXP_FLT_EXPORT_VISITOR_H__ 1 + +#include +#include "ExportOptions.h" +#include "Types.h" +#include +#include +#include + +namespace osg { + class DrawArrays; + class DrawArrayLengths; + class DrawElements; + class Geometry; + class StateSet; + class Switch; + class Material; + class Texture2D; +} +namespace osgSim { + class DOFTransform; + class MultiSwitch; + class LightPointNode; + class ObjectRecordData; +} + +namespace flt +{ + + +class ExportOptions; +class DataOutputStream; +class MaterialPaletteManager; +class TexturePaletteManager; +class VertexPaletteManager; +class LightSourcePaletteManager; + + + +/*! + The main NodeVisitor for walking the scene graph during export. + A collection of apply() methods is in FltExportVisitor.cpp. + The apply() methods call several other methods for writing + specific FLT record types, most of which are in exp*.cpp. + */ +class FltExportVisitor : public osg::NodeVisitor +{ +public: + FltExportVisitor( DataOutputStream* dos, ExportOptions* fltOpt ); + ~FltExportVisitor( ); + + bool complete( const osg::Node& node ); + + virtual void apply( osg::Group& node ); + virtual void apply( osg::Sequence& node ); + virtual void apply( osg::Switch& node ); + virtual void apply( osg::LOD& node ); + virtual void apply( osg::MatrixTransform& node ); + virtual void apply( osg::PositionAttitudeTransform& node ); + virtual void apply( osg::Transform& node ); + virtual void apply( osg::LightSource& node ); + virtual void apply( osg::Geode& node ); + virtual void apply( osg::Billboard& node ); + virtual void apply( osg::Node& node ); + virtual void apply( osg::ProxyNode& node ); + + + + // Primary records + void writeHeader( const std::string& headerName ); + void writeGroup( const osg::Group& node ); + void writeGroup( const osg::Group& group, + int32 flags, + int32 loopCount, + float32 loopDuration, + float32 lastFrameDuration); + void writeSequence( const osg::Sequence& node ); + void writeObject( const osg::Group& node, osgSim::ObjectRecordData* ord ); + void writeDegreeOfFreedom( const osgSim::DOFTransform* dof ); + void writeExternalReference( const osg::ProxyNode& node ); + void writeLevelOfDetail( const osg::LOD& lod, const osg::Vec3d& center, + double switchInDist, double switchOutDist); + void writeLightSource( const osg::LightSource& ls ); + void writeSwitch( const osgSim::MultiSwitch* ms ); + void writeSwitch( const osg::Switch* ms ); + void writeLightPoint( const osgSim::LightPointNode* lpn ); + + // Ancillary records + void writeComment( const osg::Node& node, DataOutputStream* dos=NULL ); + void writeLongID( const std::string& id, DataOutputStream* dos=NULL ); + void writeMatrix( const osg::Referenced* ref ); + void writeContinuationRecord( const unsigned short length ); + + // Control records + void writePush(); + void writePop(); + void writePushSubface(); + void writePopSubface(); + + // Helper routine for traversing a pushed subtree + void writePushTraverseWritePop(osg::Node& node) + { + writePush(); + traverse(node); + writePop(); + } + + // Geometry records + void writeFace( const osg::Geode& geode, const osg::Geometry& geom, GLenum mode ); + void writeMesh( const osg::Geode& geode, const osg::Geometry& geom, GLenum mode ); + int writeVertexList( int first, unsigned int count ); + int writeVertexList( const std::vector& indices, unsigned int count ); + void writeMeshPrimitive( const std::vector& indices, GLenum mode ); + void writeLocalVertexPool( const osg::Geometry& geom, GLenum mode ); + void writeMultitexture( const osg::Geometry& geom ); + void writeUVList( int numVerts, const osg::Geometry& geom ); + + // Light Point records + void writeLightPoint(); + + // Exporter doesn't currently support color palette; write a dummy in its place to + // support loaders that require it. + void writeColorPalette(); + + // StateSet stack support + void pushStateSet( const osg::StateSet* rhs ); + void popStateSet(); + const osg::StateSet* getCurrentStateSet() const; + void clearStateSetStack(); + + // Write a .attr file if none exists in the data file path. + void writeATTRFile( int unit, const osg::Texture2D* texture ) const; + + +private: + // Methods for handling different primitive set types. + // These are defined in expGeometryRecords.cpp. + void handleDrawArrays( const osg::DrawArrays* da, const osg::Geometry& geom, const osg::Geode& geode ); + void handleDrawArrayLengths( const osg::DrawArrayLengths* dal, const osg::Geometry& geom, const osg::Geode& geode ); + void handleDrawElements( const osg::DrawElements* de, const osg::Geometry& geom, const osg::Geode& geode ); + + bool isLit( const osg::Geometry& geom ) const; + bool isTextured( int unit, const osg::Geometry& geom ) const; + bool isAllMesh( const osg::Geometry& geom ) const; + + osg::ref_ptr< ExportOptions > _fltOpt; + + // _dos is the primary output stream, produces the actual .flt file. + DataOutputStream& _dos; + + // _records is a temp file for most records. After the Header and palette + // records are written to _dos, _records is copied onto _dos. + std::ofstream _recordsStr; + DataOutputStream* _records; + std::string _recordsTempName; + + // Track state changes during a scene graph walk. + typedef std::vector< osg::ref_ptr > StateSetStack; + StateSetStack _stateSetStack; + + std::auto_ptr _materialPalette; + std::auto_ptr _texturePalette; + std::auto_ptr _lightSourcePalette; + std::auto_ptr _vertexPalette; + + // Used to avoid duplicate Header/Group records at top of output FLT file. + bool _firstNode; +}; + +/*! + Helper class to ensure symmetrical state push/pop behavior. + */ +class ScopedStatePushPop +{ + public: + ScopedStatePushPop ( FltExportVisitor * fnv, const osg::StateSet *ss ) : + fnv_( fnv ) + { + fnv_->pushStateSet( ss ); + } + virtual ~ScopedStatePushPop () + { + fnv_->popStateSet(); + } + + private: + FltExportVisitor * fnv_; +}; + + +/*! + Automatically handles writing the LongID ancillary record. + */ +struct IdHelper +{ + IdHelper(flt::FltExportVisitor& v, const std::string& id) + : v_(v), id_(id), dos_(NULL) + { } + + // Write an ancillary ID record upon destruction if name is too long + ~IdHelper() + { + if (id_.length() > 8) + v_.writeLongID(id_,dos_); + } + + // Allow implicit conversion to the abbreviated name string + operator const std::string() const + { + return( (id_.length() > 8) ? id_.substr(0, 8) : id_ ); + } + + flt::FltExportVisitor& v_; + const std::string id_; + DataOutputStream* dos_; +}; + + +/*! + Supports wrapping subfaces with push/pop records. + */ +struct SubfaceHelper +{ + SubfaceHelper(flt::FltExportVisitor& v, const osg::StateSet* ss ) + : v_(v) + { + _polygonOffsetOn = ( ss->getMode( GL_POLYGON_OFFSET_FILL ) == osg::StateAttribute::ON ); + if (_polygonOffsetOn) + v_.writePushSubface(); + } + + ~SubfaceHelper() + { + if (_polygonOffsetOn) + v_.writePopSubface(); + } + + flt::FltExportVisitor& v_; + bool _polygonOffsetOn; +}; + +} + +#endif diff --git a/src/osgPlugins/OpenFlight/FltWriteResult.h b/src/osgPlugins/OpenFlight/FltWriteResult.h new file mode 100644 index 000000000..545d33304 --- /dev/null +++ b/src/osgPlugins/OpenFlight/FltWriteResult.h @@ -0,0 +1,73 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLT_WRITE_RESULT_H__ +#define __FLT_WRITE_RESULT_H__ 1 + +#include +#include +#include + +#include +#include +#include +#include + + +namespace flt +{ + + +/*! + Custom WriteResult to support proxy/validation ("validate" Option). + If the application is able to #include this header and obtain the Writeresult + from osgDB, then the app can query this class for warning or error + conditions due to scene graph incompatibility with FLT. + */ +class FltWriteResult : public osgDB::ReaderWriter::WriteResult +{ +public: + FltWriteResult( WriteResult::WriteStatus status=WriteResult::FILE_NOT_HANDLED ) + : WriteResult( status ) + {} + + void setNumErrors( int n ); + int getNumErrors() const; + + void setNumWarnings( int n ); + int getNumWarnings() const; + + typedef std::pair< int, std::string > MessagePair; + typedef std::vector< MessagePair > MessageVector; + + void warn( const std::string &ss ) + { + messages_.push_back( std::make_pair( osg::WARN, ss ) ); + } + + void error( const std::string &ss ) + { + messages_.push_back( std::make_pair( osg::FATAL, ss ) ); + } + +protected: + MessageVector messages_; +}; + + +} + +#endif /* __OPEN_FLIGHT_WRITER_H__ */ diff --git a/src/osgPlugins/OpenFlight/LightSourcePaletteManager.cpp b/src/osgPlugins/OpenFlight/LightSourcePaletteManager.cpp new file mode 100644 index 000000000..1607281e3 --- /dev/null +++ b/src/osgPlugins/OpenFlight/LightSourcePaletteManager.cpp @@ -0,0 +1,116 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "LightSourcePaletteManager.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include +#include +#include +#include +#include + +namespace flt +{ + + +LightSourcePaletteManager::LightSourcePaletteManager( ExportOptions& fltOpt ) + : _fltOpt( fltOpt ), + _currIndex( -1 ) +{ + // TODO: Pay attention to the version here(?) +} + + +int LightSourcePaletteManager::add(osg::Light const* light) +{ + int index = -1; + if (light == NULL) return -1; + + + // If this light has already been cached, set 'index' to the cached value + LightPalette::const_iterator it = _lightPalette.find(light); + if ( it != _lightPalette.end() ) + { + index = it->second.Index; + } + + // New light? Add it to the cache... + else + { + index = ++_currIndex; + _lightPalette.insert(std::make_pair(light, + LightRecord(light, index) ) ); + } + + return index; +} + +void +LightSourcePaletteManager::write( DataOutputStream& dos ) const +{ + using osg::Vec4f; + + static int const INFINITE_LIGHT = 0; + static int const LOCAL_LIGHT = 1; + static int const SPOT_LIGHT = 2; + + LightPalette::const_iterator it = _lightPalette.begin(); + for ( ; it != _lightPalette.end(); ++it) + { + LightRecord m = it->second; + + static char lightName[64]; + sprintf(lightName, "Light%02d", m.Light->getLightNum() ); + + int lightType = INFINITE_LIGHT; + Vec4f const& lightPos = m.Light->getPosition(); + if (lightPos.w() != 0) + { + if (m.Light->getSpotCutoff() < 180) + lightType = SPOT_LIGHT; + else + lightType = LOCAL_LIGHT; + } + + dos.writeInt16( (int16) LIGHT_SOURCE_PALETTE_OP ); + dos.writeInt16( 240 ); + dos.writeInt32( m.Index ); + dos.writeFill(2*4, '\0'); // Reserved + dos.writeString( lightName, 20 ); + dos.writeFill(4, '\0'); // Reserved + + dos.writeVec4f(m.Light->getAmbient() ); + dos.writeVec4f(m.Light->getDiffuse() ); + dos.writeVec4f(m.Light->getSpecular() ); + dos.writeInt32(lightType); + dos.writeFill(4*10, '\0'); // Reserved + dos.writeFloat32(m.Light->getSpotExponent() ); + dos.writeFloat32(m.Light->getSpotCutoff() ); + dos.writeFloat32(0); // Yaw (N/A) + dos.writeFloat32(0); // Pitch (N/A) + dos.writeFloat32(m.Light->getConstantAttenuation() ); + dos.writeFloat32(m.Light->getLinearAttenuation() ); + dos.writeFloat32(m.Light->getQuadraticAttenuation() ); + dos.writeInt32(0); // Modeling flag (N/A) + dos.writeFill(4*19, '\0'); // Reserved + + } +} + + + +} // End namespace fltexp diff --git a/src/osgPlugins/OpenFlight/LightSourcePaletteManager.h b/src/osgPlugins/OpenFlight/LightSourcePaletteManager.h new file mode 100644 index 000000000..0dee959b4 --- /dev/null +++ b/src/osgPlugins/OpenFlight/LightSourcePaletteManager.h @@ -0,0 +1,74 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_LIGHT_SOURCE_PALETTE_MANAGER_H__ +#define __FLTEXP_LIGHT_SOURCE_PALETTE_MANAGER_H__ 1 + +#include "ExportOptions.h" + +#include + +namespace osg { + class Light; +} + + +namespace flt +{ + + +class DataOutputStream; + + +class LightSourcePaletteManager +{ + public: + LightSourcePaletteManager( ExportOptions& fltOpt ); + + // Add a light to the palette and auto-assign it the next available index + int add(osg::Light const* light); + + // Write the light palette records out to a DataOutputStream + void write( DataOutputStream& dos ) const; + + + private: + int _currIndex; + + // Helper struct to hold light palette records + struct LightRecord + { + LightRecord(osg::Light const* light, int i) + : Light(light) + , Index(i) { } + + osg::Light const* Light; + int Index; + + }; + + // Map to allow lookups by Light pointer + typedef std::map LightPalette; + LightPalette _lightPalette; + + + ExportOptions& _fltOpt; +}; + + +} // End namespace fltexp + +#endif // !__FLTEXP_LIGHT_SOURCE_PALETTE_MANAGER_H__ diff --git a/src/osgPlugins/OpenFlight/MaterialPaletteManager.cpp b/src/osgPlugins/OpenFlight/MaterialPaletteManager.cpp new file mode 100644 index 000000000..56607a1d5 --- /dev/null +++ b/src/osgPlugins/OpenFlight/MaterialPaletteManager.cpp @@ -0,0 +1,120 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "MaterialPaletteManager.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include +#include +#include +#include +#include + +namespace flt +{ + + +MaterialPaletteManager::MaterialPaletteManager( ExportOptions& fltOpt ) + : _fltOpt( fltOpt ), + _currIndex( -1 ) +{ + // TODO: Pay attention to the version here(?) +} + + +int MaterialPaletteManager::add(osg::Material const* material) +{ + int index = -1; + if (material == NULL) return -1; + + + // If this material has already been cached, set 'index' to the cached value + MaterialPalette::const_iterator it = _materialPalette.find(material); + if ( it != _materialPalette.end() ) + { + index = it->second.Index; + } + + // New material? Add it to the cache... + else + { + index = ++_currIndex; + _materialPalette.insert(std::make_pair(material, + MaterialRecord(material, index) ) ); + } + + return index; +} + +void +MaterialPaletteManager::write( DataOutputStream& dos ) const +{ + using osg::Vec4f; + + MaterialPalette::const_iterator it = _materialPalette.begin(); + for ( ; it != _materialPalette.end(); ++it) + { + MaterialRecord m = it->second; + Vec4f const& ambient = m.Material->getAmbient(osg::Material::FRONT); + Vec4f const& diffuse = m.Material->getDiffuse(osg::Material::FRONT); + Vec4f const& specular = m.Material->getSpecular(osg::Material::FRONT); + Vec4f const& emissive = m.Material->getEmission(osg::Material::FRONT); + float shininess = m.Material->getShininess(osg::Material::FRONT); + + dos.writeInt16( (int16) MATERIAL_PALETTE_OP ); + dos.writeInt16( 84 ); // Length - FIXME: hard-code/FLT version? + dos.writeInt32( m.Index ); + std::string fakename = "SOMEFAKENAME"; + dos.writeString( fakename, 12 ); // Name - FIXME: put a 'real' name here? + dos.writeInt32( 0 ); // Flags + dos.writeFloat32(ambient.r() ); + dos.writeFloat32(ambient.g() ); + dos.writeFloat32(ambient.b() ); + dos.writeFloat32(diffuse.r() ); + dos.writeFloat32(diffuse.g() ); + dos.writeFloat32(diffuse.b() ); + dos.writeFloat32(specular.r() ); + dos.writeFloat32(specular.g() ); + dos.writeFloat32(specular.b() ); + dos.writeFloat32(emissive.r() ); + dos.writeFloat32(emissive.g() ); + dos.writeFloat32(emissive.b() ); + dos.writeFloat32(shininess); + dos.writeFloat32(1.0f); // Alpha - unused + dos.writeFloat32(1.0f); // 'Reserved' - unused + + if (m.Material->getAmbientFrontAndBack() == false || + m.Material->getDiffuseFrontAndBack() == false || + m.Material->getSpecularFrontAndBack() == false || + m.Material->getEmissionFrontAndBack() == false || + m.Material->getShininessFrontAndBack() == false ) + + { + std::stringstream ss; + ss << "Front and back faces of material \'" + << fakename + << "\' have different material" + << "properties - OpenFlt does not support this..." + << std::endl; + _fltOpt.getWriteResult().warn( ss.str() ); + } + + } +} + + + +} // End namespace fltexp diff --git a/src/osgPlugins/OpenFlight/MaterialPaletteManager.h b/src/osgPlugins/OpenFlight/MaterialPaletteManager.h new file mode 100644 index 000000000..5d94584fc --- /dev/null +++ b/src/osgPlugins/OpenFlight/MaterialPaletteManager.h @@ -0,0 +1,72 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_MATERIAL_PALETTE_MANAGER_H__ +#define __FLTEXP_MATERIAL_PALETTE_MANAGER_H__ 1 + +#include "ExportOptions.h" +#include + +namespace osg { + class Material; +} + + +namespace flt +{ + + +class DataOutputStream; + + +class MaterialPaletteManager +{ + public: + MaterialPaletteManager( ExportOptions& fltOpt ); + + // Add a material to the palette and auto-assign it the next available index + int add(osg::Material const* material); + + // Write the material palette records out to a DataOutputStream + void write( DataOutputStream& dos ) const; + + + private: + int _currIndex; + + // Helper struct to hold material palette records + struct MaterialRecord + { + MaterialRecord(osg::Material const* m, int i) + : Material(m) + , Index(i) { } + + osg::Material const* Material; + int Index; + + }; + + // Map to allow lookups by Material pointer, and permit sorting by index + typedef std::map MaterialPalette; + MaterialPalette _materialPalette; + + ExportOptions& _fltOpt; +}; + + +} // End namespace fltexp + +#endif // !FLTEXP_MATERIAL_PALETTE_MANAGER_H diff --git a/src/osgPlugins/OpenFlight/Opcodes.h b/src/osgPlugins/OpenFlight/Opcodes.h new file mode 100644 index 000000000..510e69814 --- /dev/null +++ b/src/osgPlugins/OpenFlight/Opcodes.h @@ -0,0 +1,136 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// OpenFlight® loader for OpenSceneGraph +// +// Copyright (C) 2005-2007 Brede Johansen +// + +#ifndef FLT_OPCODES_H +#define FLT_OPCODES_H + +namespace flt { + +enum Opcodes +{ + UNKNOWN_OP = 0, + HEADER_OP = 1, + GROUP_OP = 2, + OLD_LOD_OP = 3, + OBJECT_OP = 4, + FACE_OP = 5, + OLD_ABSOLUTE_VERTEX_OP = 7, + OLD_SHADED_VERTEX_OP = 8, + OLD_NORMAL_VERTEX_OP = 9, + PUSH_LEVEL_OP = 10, + POP_LEVEL_OP = 11, + DOF_OP = 14, + PUSH_SUBFACE_OP = 19, + POP_SUBFACE_OP = 20, + PUSH_EXTENSION_OP = 21, + POP_EXTENSION_OP = 22, + CONTINUATION_OP = 23, + COMMENT_OP = 31, + COLOR_PALETTE_OP = 32, + LONG_ID_OP = 33, + OLD_TRANSLATE_OP = 40, + OLD_ROTATE_ABOUT_POINT_OP = 41, + OLD_ROTATE_ABOUT_EDGE_OP = 42, + OLD_SCALE_OP = 43, + OLD_TRANSLATE2_OP = 44, + OLD_NONUNIFORM_SCALE_OP = 45, + OLD_ROTATE_ABOUT_POINT2_OP = 46, + OLD_ROTATE_SCALE_TO_POINT_OP = 47, + OLD_PUT_TRANSFORM_OP = 48, + MATRIX_OP = 49, + VECTOR_OP = 50, + OLD_BOUNDING_BOX_OP = 51, + MULTITEXTURE_OP = 52, + UV_LIST_OP = 53, + BINARY_SEPARATING_PLANE_OP = 55, + REPLICATE_OP = 60, + INSTANCE_REFERENCE_OP = 61, + INSTANCE_DEFINITION_OP = 62, + EXTERNAL_REFERENCE_OP = 63, + TEXTURE_PALETTE_OP = 64, + OLD_EYEPOINT_PALETTE_OP = 65, + OLD_MATERIAL_PALETTE_OP = 66, + VERTEX_PALETTE_OP = 67, + VERTEX_C_OP = 68, + VERTEX_CN_OP = 69, + VERTEX_CNT_OP = 70, + VERTEX_CT_OP = 71, + VERTEX_LIST_OP = 72, + LOD_OP = 73, + BOUNDING_BOX_OP = 74, + ROTATE_ABOUT_EDGE_OP = 76, + SCALE_OP = 77, + TRANSLATE_OP = 78, + NONUNIFORM_SCALE_OP = 79, + ROTATE_ABOUT_POINT_OP = 80, + ROTATE_SCALE_TO_POINT_OP = 81, + PUT_TRANSFORM_OP = 82, + EYEPOINT_AND_TRACKPLANE_PALETTE_OP = 83, + MESH_OP = 84, + LOCAL_VERTEX_POOL_OP = 85, + MESH_PRIMITIVE_OP = 86, + ROAD_SEGMENT_OP = 87, + ROAD_ZONE_OP = 88, + MORPH_VERTEX_LIST_OP = 89, + LINKAGE_PALETTE_OP = 90, + SOUND_OP = 91, + ROAD_PATH_OP = 92, + SOUND_PALETTE_OP = 93, + GENERAL_MATRIX_OP = 94, + TEXT_OP = 95, + SWITCH_OP = 96, + LINE_STYLE_PALETTE_OP = 97, + CLIP_REGION_OP = 98, + EXTENSION_OP = 100, + LIGHT_SOURCE_OP = 101, + LIGHT_SOURCE_PALETTE_OP = 102, + BOUNDING_SPHERE_OP = 105, + BOUNDING_CYLINDER_OP = 106, + BOUNDING_CONVEX_HULL_OP = 107, + BOUNDING_VOLUME_CENTER_OP = 108, + BOUNDING_VOLUME_ORIENTATION_OP = 109, + HISTOGRAM_BOUNDING_VOLUME_OP = 110, + LIGHT_POINT_OP = 111, + TEXTURE_MAPPING_PALETTE_OP = 112, + MATERIAL_PALETTE_OP = 113, + NAME_TABLE_OP = 114, + CAT_OP = 115, + CAT_DATA_OP = 116, + BOUNDING_HISTOGRAM = 119, + PUSH_ATTRIBUTE_OP = 122, + POP_ATTRIBUTE_OP = 123, + ADAPTIVE_ATTRIBUTE_OP = 125, + CURVE_NODE_OP = 126, + ROAD_CONSTRUCTION_OP = 127, + LIGHT_POINT_APPEARANCE_PALETTE_OP = 128, + LIGHT_POINT_ANIMATION_PALETTE_OP = 129, + INDEXED_LIGHT_POINT_OP = 130, + LIGHT_POINT_SYSTEM_OP = 131, + INDEXED_STRING_OP = 132, + SHADER_PALETTE_OP = 133 +}; + + +} // end namespace + +#endif + + + + diff --git a/src/osgPlugins/OpenFlight/Pools.h b/src/osgPlugins/OpenFlight/Pools.h index 5c34952ed..73ea12e80 100644 --- a/src/osgPlugins/OpenFlight/Pools.h +++ b/src/osgPlugins/OpenFlight/Pools.h @@ -28,7 +28,7 @@ #include #include #include -#include "types.h" +#include "Types.h" namespace flt { diff --git a/src/osgPlugins/OpenFlight/ReaderWriterATTR.cpp b/src/osgPlugins/OpenFlight/ReaderWriterATTR.cpp index ae26f2840..64ff0d224 100644 --- a/src/osgPlugins/OpenFlight/ReaderWriterATTR.cpp +++ b/src/osgPlugins/OpenFlight/ReaderWriterATTR.cpp @@ -29,6 +29,7 @@ #include "AttrData.h" #include "DataInputStream.h" +#include "DataOutputStream.h" using namespace osg; using namespace osgDB; @@ -47,6 +48,7 @@ class ReaderWriterATTR : public osgDB::ReaderWriter } virtual ReadResult readObject(const std::string& fileName, const ReaderWriter::Options*) const; + virtual ReaderWriter::WriteResult writeObject(const osg::Object& object, const std::string& fileName, const Options* options) const; }; @@ -74,7 +76,7 @@ ReaderWriter::ReadResult ReaderWriterATTR::readObject(const std::string& file, c try { attr->texels_u = in.readInt32(); - attr->textel_v = in.readInt32(); + attr->texels_v = in.readInt32(); attr->direction_u = in.readInt32(); attr->direction_v = in.readInt32(); attr->x_up = in.readInt32(); @@ -170,7 +172,7 @@ ReaderWriter::ReadResult ReaderWriterATTR::readObject(const std::string& file, c in.forward(14*4); attr->attrVersion = in.readInt32(); attr->controlPoints = in.readInt32(); - in.forward(4); + attr->numSubtextures = in.readInt32(); #endif } catch(...) @@ -187,19 +189,125 @@ ReaderWriter::ReadResult ReaderWriterATTR::readObject(const std::string& file, c } +ReaderWriter::WriteResult +ReaderWriterATTR::writeObject(const osg::Object& object, const std::string& fileName, const Options* options) const +{ + using std::ios; + + std::string ext = osgDB::getLowerCaseFileExtension( fileName ); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + + const AttrData* attr = dynamic_cast< const AttrData* >( &object ); + if (attr == NULL) + { + osg::notify( osg::FATAL ) << "AttrWriter: Invalid Object." << std::endl; + return WriteResult::FILE_NOT_HANDLED; + } + + std::ofstream fOut; + fOut.open( fileName.c_str(), std::ios::out | std::ios::binary ); + + if ( fOut.fail()) + return WriteResult::ERROR_IN_WRITING_FILE; + + flt::DataOutputStream out( fOut.rdbuf() ); + + + out.writeInt32( attr->texels_u ); + out.writeInt32( attr->texels_v ); + out.writeInt32( attr->direction_u ); + out.writeInt32( attr->direction_v ); + out.writeInt32( attr->x_up ); + out.writeInt32( attr->y_up ); + out.writeInt32( attr->fileFormat ); + out.writeInt32( attr->minFilterMode ); + out.writeInt32( attr->magFilterMode ); + out.writeInt32( attr->wrapMode ); + + out.writeInt32( attr->wrapMode_u ); + out.writeInt32( attr->wrapMode_v ); + + out.writeInt32( attr->modifyFlag ); + out.writeInt32( attr->pivot_x ); + out.writeInt32( attr->pivot_y ); + + out.writeInt32( attr->texEnvMode ); + out.writeInt32( attr->intensityAsAlpha ); + out.writeFill( 4*8 ); + out.writeFloat64( attr->size_u ); + out.writeFloat64( attr->size_v ); + out.writeInt32( attr->originCode ); + out.writeInt32( attr->kernelVersion ); + out.writeInt32( attr->intFormat ); + out.writeInt32( attr->extFormat ); + out.writeInt32( attr->useMips ); + for (int n=0; n<8; n++) + out.writeFloat32( attr->of_mips[n] ); + out.writeInt32( attr->useLodScale ); + out.writeFloat32( attr->lod0 ); + out.writeFloat32( attr->scale0 ); + out.writeFloat32( attr->lod1 ); + out.writeFloat32( attr->scale1 ); + out.writeFloat32( attr->lod2 ); + out.writeFloat32( attr->scale2 ); + out.writeFloat32( attr->lod3 ); + out.writeFloat32( attr->scale3 ); + out.writeFloat32( attr->lod4 ); + out.writeFloat32( attr->scale4 ); + out.writeFloat32( attr->lod5 ); + out.writeFloat32( attr->scale5 ); + out.writeFloat32( attr->lod6 ); + out.writeFloat32( attr->scale6 ); + out.writeFloat32( attr->lod7 ); + out.writeFloat32( attr->scale7 ); + out.writeFloat32( attr->clamp ); + out.writeInt32( attr->magFilterAlpha ); + out.writeInt32( attr->magFilterColor ); + out.writeFill( 4 ); + out.writeFill( 4*8 ); + out.writeFloat64( attr->lambertMeridian ); + out.writeFloat64( attr->lambertUpperLat ); + out.writeFloat64( attr->lambertlowerLat ); + out.writeFill( 8 ); + out.writeFill( 4*5 ); + out.writeInt32( attr->useDetail ); + out.writeInt32( attr->txDetail_j ); + out.writeInt32( attr->txDetail_k ); + out.writeInt32( attr->txDetail_m ); + out.writeInt32( attr->txDetail_n ); + out.writeInt32( attr->txDetail_s ); + out.writeInt32( attr->useTile ); + out.writeFloat32( attr->txTile_ll_u ); + out.writeFloat32( attr->txTile_ll_v ); + out.writeFloat32( attr->txTile_ur_u ); + out.writeFloat32( attr->txTile_ur_v ); + out.writeInt32( attr->projection ); + out.writeInt32( attr->earthModel ); + out.writeFill( 4 ); + out.writeInt32( attr->utmZone ); + out.writeInt32( attr->imageOrigin ); + out.writeInt32( attr->geoUnits ); + out.writeFill( 4 ); + out.writeFill( 4 ); + out.writeInt32( attr->hemisphere ); + out.writeFill( 4 ); + out.writeFill( 4 ); + out.writeFill( 149*4 ); + out.writeString( attr->comments, 512 ); + + out.writeFill( 13*4 ); + out.writeInt32( attr->attrVersion ); + out.writeInt32( attr->controlPoints ); + out.writeInt32( attr->numSubtextures ); + + + fOut.close(); + + return WriteResult::FILE_SAVED; +} + + // now register with Registry to instantiate the above // reader/writer. REGISTER_OSGPLUGIN(attr, ReaderWriterATTR) - - - - - - - - - - - - diff --git a/src/osgPlugins/OpenFlight/ReaderWriterFLT.cpp b/src/osgPlugins/OpenFlight/ReaderWriterFLT.cpp index 71655b6e5..a9512ccfc 100644 --- a/src/osgPlugins/OpenFlight/ReaderWriterFLT.cpp +++ b/src/osgPlugins/OpenFlight/ReaderWriterFLT.cpp @@ -30,6 +30,9 @@ #include "Registry.h" #include "Document.h" #include "RecordInputStream.h" +#include "DataOutputStream.h" +#include "FltExportVisitor.h" +#include "ExportOptions.h" #define SERIALIZER() OpenThreads::ScopedLock lock(_serializerMutex) @@ -81,9 +84,44 @@ public: +/*! + +FLTReaderWriter supports importing and exporting OSG scene grqphs +from/to OpenFlight files. + + + + + + + + + + + + + + + + + + + + + + + +
NodeObjectImageHeightField
ReadX
WriteX
+ +*/ + class FLTReaderWriter : public ReaderWriter { public: + FLTReaderWriter() + : _implicitPath( "." ) + {} + virtual const char* className() const { return "FLT Reader/Writer"; } virtual bool acceptsExtension(const std::string& extension) const @@ -347,11 +385,38 @@ class FLTReaderWriter : public ReaderWriter return WriteResult::FILE_NOT_HANDLED; } - virtual WriteResult writeNode(const Node& /*node*/,const std::string& /*fileName*/, const osgDB::ReaderWriter::Options* /*options*/) const + virtual WriteResult writeNode( const osg::Node& node, const std::string& fileName, const Options* options ) const { - return WriteResult::FILE_NOT_HANDLED; + if ( fileName.empty() ) + { + osg::notify( osg::FATAL ) << "fltexp: writeNode: empty file name" << std::endl; + return WriteResult::FILE_NOT_HANDLED; + } + std::string ext = osgDB::getLowerCaseFileExtension( fileName ); + if ( !acceptsExtension(ext) ) + return WriteResult::FILE_NOT_HANDLED; + + // Get and save the implicit path name (in case a path wasn't specified in Options). + std::string filePath = osgDB::getFilePath( fileName ); + if (!filePath.empty()) + _implicitPath = filePath; + + std::ofstream fOut; + fOut.open( fileName.c_str(), std::ios::out | std::ios::binary ); + if ( fOut.fail()) + { + osg::notify( osg::FATAL ) << "fltexp: Failed to open output stream." << std::endl; + return WriteResult::ERROR_IN_WRITING_FILE; + } + + WriteResult wr = WriteResult::FILE_NOT_HANDLED; + wr = writeNode( node, fOut, options ); + fOut.close(); + + return wr; } - + + virtual WriteResult writeObject(const Object& object,std::ostream& fout, const osgDB::ReaderWriter::Options* options) const { const Node* node = dynamic_cast(&object); @@ -359,12 +424,42 @@ class FLTReaderWriter : public ReaderWriter return WriteResult::FILE_NOT_HANDLED; } - virtual WriteResult writeNode(const Node& /*node*/,std::ostream& /*fout*/, const osgDB::ReaderWriter::Options* /*options*/) const + virtual WriteResult writeNode( const osg::Node& node, std::ostream& fOut, const Options* options ) const { - return WriteResult::FILE_NOT_HANDLED; + // Convert Options to FltOptions. + ExportOptions* fltOpt = new ExportOptions( options ); + fltOpt->parseOptionsString(); + + // If user didn't specify a temp dir, use the output directory + // that was implicit in the output file name. + if (fltOpt->getTempDir().empty()) + fltOpt->setTempDir( _implicitPath ); + if (!fltOpt->getTempDir().empty()) + { + // If the temp directory doesn't already exist, make it. + if ( !osgDB::makeDirectory( fltOpt->getTempDir() ) ) + { + osg::notify( osg::FATAL ) << "fltexp: Error creating temp dir: " << fltOpt->getTempDir() << std::endl; + return WriteResult::ERROR_IN_WRITING_FILE; + } + } + + flt::DataOutputStream dos( fOut.rdbuf(), fltOpt->getValidateOnly() ); + flt::FltExportVisitor fnv( &dos, fltOpt ); + + // Hm. 'node' is const, but in order to write out this scene graph, + // must use Node::accept() which requires 'node' to be non-const. + // Pretty much requires casting away const. + osg::Node* nodeNonConst = const_cast( &node ); + nodeNonConst->accept( fnv ); + fnv.complete( node ); + + // FIXME: Error-handling? + return WriteResult::FILE_SAVED; } protected: + mutable std::string _implicitPath; mutable OpenThreads::ReentrantMutex _serializerMutex; }; @@ -372,16 +467,3 @@ class FLTReaderWriter : public ReaderWriter // now register with Registry to instantiate the above // reader/writer. REGISTER_OSGPLUGIN(OpenFlight, FLTReaderWriter) - - - - - - - - - - - - - diff --git a/src/osgPlugins/OpenFlight/RecordInputStream.cpp b/src/osgPlugins/OpenFlight/RecordInputStream.cpp index 58bab1e33..f23971dd7 100644 --- a/src/osgPlugins/OpenFlight/RecordInputStream.cpp +++ b/src/osgPlugins/OpenFlight/RecordInputStream.cpp @@ -18,7 +18,7 @@ // #include -#include "opcodes.h" +#include "Opcodes.h" #include "Registry.h" #include "Document.h" #include "RecordInputStream.h" diff --git a/src/osgPlugins/OpenFlight/Registry.h b/src/osgPlugins/OpenFlight/Registry.h index 7c1a0169d..49d53cc54 100644 --- a/src/osgPlugins/OpenFlight/Registry.h +++ b/src/osgPlugins/OpenFlight/Registry.h @@ -23,7 +23,7 @@ #include #include #include -#include "opcodes.h" +#include "Opcodes.h" #include "Record.h" namespace flt { diff --git a/src/osgPlugins/OpenFlight/TexturePaletteManager.cpp b/src/osgPlugins/OpenFlight/TexturePaletteManager.cpp new file mode 100644 index 000000000..76940a181 --- /dev/null +++ b/src/osgPlugins/OpenFlight/TexturePaletteManager.cpp @@ -0,0 +1,99 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "TexturePaletteManager.h" +#include "FltExportVisitor.h" +#include "ExportOptions.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include +#include +#include +#include + + +namespace flt +{ + + +TexturePaletteManager::TexturePaletteManager( const FltExportVisitor& nv, const ExportOptions& fltOpt ) + : _fltOpt( fltOpt ), + _nv( nv ), + _currIndex( 0 ) +{ +} + +int +TexturePaletteManager::add( int unit, const osg::Texture2D* texture ) +{ + if( (!texture) || + (!texture->getImage()) ) + return -1; + + int index( -1 ); + TextureIndexMap::const_iterator it = _indexMap.find( texture ); + if (it != _indexMap.end()) + index = it->second; + else + { + index = _currIndex++; + _indexMap[ texture ] = index; + + // If there is no .attr file, write one + _nv.writeATTRFile( unit, texture ); + } + + return index; +} + +void +TexturePaletteManager::write( DataOutputStream& dos ) const +{ + int x( 0 ), y( 0 ), height( 0 ); + TextureIndexMap::const_iterator it = _indexMap.begin(); + while (it != _indexMap.end()) + { + const osg::Texture2D* texture = it->first; + int index = it->second; + + std::string fileName; + if ( _fltOpt.getStripTextureFilePath() ) + fileName = osgDB::getSimpleFileName( texture->getImage()->getFileName() ); + else + fileName = texture->getImage()->getFileName(); + + dos.writeInt16( (int16) TEXTURE_PALETTE_OP ); + dos.writeUInt16( 216 ); + dos.writeString( fileName, 200 ); + dos.writeInt32( index ); + dos.writeInt32( x ); + dos.writeInt32( y ); + it++; + + x += texture->getImage()->s(); + if (texture->getImage()->t() > height) + height = texture->getImage()->t(); + if (x > 1024) + { + x = 0; + y += height; + height = 0; + } + } +} + + +} diff --git a/src/osgPlugins/OpenFlight/TexturePaletteManager.h b/src/osgPlugins/OpenFlight/TexturePaletteManager.h new file mode 100644 index 000000000..f998d0f10 --- /dev/null +++ b/src/osgPlugins/OpenFlight/TexturePaletteManager.h @@ -0,0 +1,62 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_TEXTURE_PALETTE_MANAGER_H__ +#define __FLTEXP_TEXTURE_PALETTE_MANAGER_H__ 1 + + +#include "ExportOptions.h" +#include +#include + +namespace osg { + class Texture2D; +} + + +namespace flt +{ + + +class DataOutputStream; +class FltExportVisitor; + + +class TexturePaletteManager +{ +public: + TexturePaletteManager( const FltExportVisitor& nv, const ExportOptions& fltOpt ); + + int add( int unit, const osg::Texture2D* texture ); + + void write( DataOutputStream& dos ) const; + +protected: + int _currIndex; + + typedef std::map< const osg::Texture2D*, int > TextureIndexMap; + TextureIndexMap _indexMap; + + const FltExportVisitor& _nv; + + const ExportOptions& _fltOpt; +}; + + + +} + +#endif diff --git a/src/osgPlugins/OpenFlight/Types.h b/src/osgPlugins/OpenFlight/Types.h new file mode 100644 index 000000000..f7595581e --- /dev/null +++ b/src/osgPlugins/OpenFlight/Types.h @@ -0,0 +1,51 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// OpenFlight® loader for OpenSceneGraph +// +// Copyright (C) 2005-2007 Brede Johansen +// + +#ifndef FLT_TYPES_H +#define FLT_TYPES_H 1 + +namespace flt { + +#if defined(_MSC_VER) + +typedef __int8 int8; +typedef unsigned __int8 uint8; +typedef __int16 int16; +typedef unsigned __int16 uint16; +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef float float32; +typedef double float64; + +#else + +typedef signed char int8; +typedef unsigned char uint8; +typedef signed short int16; +typedef unsigned short uint16; +typedef signed int int32; +typedef unsigned int uint32; +typedef float float32; +typedef double float64; + +#endif + +} // end namespace + +#endif diff --git a/src/osgPlugins/OpenFlight/Utils.h b/src/osgPlugins/OpenFlight/Utils.h new file mode 100644 index 000000000..c083a2ed9 --- /dev/null +++ b/src/osgPlugins/OpenFlight/Utils.h @@ -0,0 +1,36 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_UTILS_H__ +#define __FLTEXP_UTILS_H__ 1 + + +// FLTEXP_DELETEFILE macro is used to delete temp files created during file export. +// (Too bad OSG doesn't use Boost.) + +#if defined(_WIN32) + + #include + #define FLTEXP_DELETEFILE(file) DeleteFile((file)) + +#else // Unix + + #include + #define FLTEXP_DELETEFILE(file) remove((file)) + +#endif + +#endif diff --git a/src/osgPlugins/OpenFlight/VertexPaletteManager.cpp b/src/osgPlugins/OpenFlight/VertexPaletteManager.cpp new file mode 100644 index 000000000..17a55bc8f --- /dev/null +++ b/src/osgPlugins/OpenFlight/VertexPaletteManager.cpp @@ -0,0 +1,342 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "VertexPaletteManager.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include "Utils.h" +#include +#include +#include + + +namespace flt +{ + + +VertexPaletteManager::VertexPaletteManager( const ExportOptions& fltOpt ) + : _fltOpt( fltOpt ), + _currentSizeBytes( 8 ), + _current( NULL ), + _vertices( NULL ) +{ +} + +VertexPaletteManager::~VertexPaletteManager() +{ + if (!_verticesTempName.empty()) + { + // Delete our temp file. + if (_verticesStr.is_open()) + { + osg::notify( osg::WARN ) << "fltexp: VertexPaletteManager destructor has an open temp file." << std::endl; + // This should not happen. FltNodeVisitor::complete should close + // this file before we get to this destructor. + return; + } + osg::notify( osg::INFO ) << "fltexp: Deleting temp file " << _verticesTempName << std::endl; + FLTEXP_DELETEFILE( _verticesTempName.c_str() ); + } +} + +void +VertexPaletteManager::add( const osg::Geometry& geom ) +{ + const osg::Array* v = geom.getVertexArray(); + const osg::Array* c = geom.getColorArray(); + const osg::Array* n = geom.getNormalArray(); + const osg::Array* t = geom.getTexCoordArray( 0 ); + // TBD eventually need to be able to support other array types. + const osg::Vec3Array* v3 = dynamic_cast( v ); + const osg::Vec4Array* c4 = dynamic_cast( c ); + const osg::Vec3Array* n3 = dynamic_cast( n ); + const osg::Vec2Array* t2 = dynamic_cast( t ); + if (v && !v3) + { + std::string warning( "fltexp: VertexPalette: VertexArray is not Vec3Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt.getWriteResult().warn( warning ); + return; + } + if (c && !c4) + { + std::string warning( "fltexp: VertexPalette: ColorArray is not Vec4Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt.getWriteResult().warn( warning ); + return; + } + if (n && !n3) + { + std::string warning( "fltexp: VertexPalette: NormalArray is not Vec3Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt.getWriteResult().warn( warning ); + return; + } + if (t && !t2) + { + std::string warning( "fltexp: VertexPalette: TexCoordArray is not Vec2Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt.getWriteResult().warn( warning ); + return; + } + + const bool cpv =( geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX ); + const bool npv =( geom.getNormalBinding() == osg::Geometry::BIND_PER_VERTEX ); + add( v3, c4, n3, t2, cpv, npv ); +} +void +VertexPaletteManager::add( const osg::Vec3Array* v, const osg::Vec4Array* c, + const osg::Vec3Array* n, const osg::Vec2Array* t, + bool colorPerVertex, bool normalPerVertex, bool allowSharing ) +{ + bool needsInit( true ); + + if (allowSharing) + { + ArrayMap::iterator it = _arrayMap.find( v ); + if (it != _arrayMap.end()) + needsInit = false; + _current = &( _arrayMap[ v ] ); + } + else + _current = &( _nonShared ); + + if (needsInit) + { + _current->_byteStart = _currentSizeBytes; + + _current->_idxCount = v->size(); + + _current->_idxSizeBytes = recordSize( recordType( v, c, n, t ) ); + _currentSizeBytes += ( _current->_idxSizeBytes * _current->_idxCount ); + + // Next we'll write the vertex palette record data. But, + // if we don't have a DataOutputStream yet, open the temp file. + if (!_vertices) + { + _verticesTempName = _fltOpt.getTempDir() + "/ofw_temp_vertices"; + _verticesStr.open( _verticesTempName.c_str(), std::ios::out | std::ios::binary ); + _vertices = new DataOutputStream( _verticesStr.rdbuf(), _fltOpt.getValidateOnly() ); + } + writeRecords( v, c, n, t, colorPerVertex, normalPerVertex ); + } +} + +unsigned int +VertexPaletteManager::byteOffset( unsigned int idx ) const +{ + if (!_current) + { + osg::notify( osg::WARN ) << "fltexp: No current vertex array in VertexPaletteManager." << std::endl; + return 4; + } + if (idx >= _current->_idxCount) + { + osg::notify( osg::WARN ) << "fltexp: Index out of range in VertexPaletteManager." << std::endl; + return 4; + } + + return( _current->_byteStart + (_current->_idxSizeBytes * idx) ); +} + +void +VertexPaletteManager::write( DataOutputStream& dos ) const +{ + if (_currentSizeBytes == 8) + // Empty palette. Don't write anything. + return; + + dos.writeInt16( (int16) VERTEX_PALETTE_OP ); + dos.writeUInt16( 8 ); + dos.writeInt32( _currentSizeBytes ); + + // Close the temp file. We're done writing new data to it. + _verticesStr.close(); + + // Open that temp file again, this time for reading. + // Then copy to dos. + char buf; + std::ifstream vertIn; + vertIn.open( _verticesTempName.c_str(), std::ios::in | std::ios::binary ); + while (!vertIn.eof() ) + { + vertIn.read( &buf, 1 ); + if (vertIn.good()) + dos << buf; + } + vertIn.close(); +} + + + +VertexPaletteManager::PaletteRecordType +VertexPaletteManager::recordType( const osg::Array* v, const osg::Array* c, + const osg::Array* n, const osg::Array* t ) +{ + if (t) + { + // Texture coordinstes + if (n) + return VERTEX_CNT; + else + return VERTEX_CT; + } + else + { + // No textue coordinates + if (n) + return VERTEX_CN; + else + return VERTEX_C; + } +} + +unsigned int +VertexPaletteManager::recordSize( PaletteRecordType recType ) +{ + switch (recType) + { + case VERTEX_C: + return 40; + break; + case VERTEX_CN: + return (_fltOpt.getFlightFileVersionNumber() > ExportOptions::VERSION_15_7) ? 56 : 52; + break; + case VERTEX_CT: + return 48; + break; + case VERTEX_CNT: + return 64; + break; + default: + return 0; + } +} + +void +VertexPaletteManager::writeRecords( const osg::Vec3Array* v, const osg::Vec4Array* c, + const osg::Vec3Array* n, const osg::Vec2Array* t, + bool colorPerVertex, bool normalPerVertex ) +{ + const PaletteRecordType recType = recordType( v, c, n, t ); + const int16 sizeBytes = recordSize( recType ); + + int16 opcode; + switch( recType ) + { + case VERTEX_C: + opcode = VERTEX_C_OP; + break; + case VERTEX_CN: + opcode = VERTEX_CN_OP; + if (!n) + osg::notify( osg::WARN ) << "fltexp: VPM::writeRecords: no normal array." << std::endl; + break; + case VERTEX_CNT: + opcode = VERTEX_CNT_OP; + if (!n) + osg::notify( osg::WARN ) << "fltexp: VPM::writeRecords: no normal array." << std::endl; + if (!t) + osg::notify( osg::WARN ) << "fltexp: VPM::writeRecords: no tex coord array." << std::endl; + break; + case VERTEX_CT: + opcode = VERTEX_CT_OP; + if (!t) + osg::notify( osg::WARN ) << "fltexp: VPM::writeRecords: no tex coord array." << std::endl; + break; + } + + enum FlagBits + { + START_HARD_EDGE = (0x8000 >> 0), + NORMAL_FROZEN = (0x8000 >> 1), + NO_COLOR = (0x8000 >> 2), + PACKED_COLOR = (0x8000 >> 3) + }; + uint32 flags( NO_COLOR ); + if (colorPerVertex) + flags = PACKED_COLOR; + + + int cIdx( 0 ); + int nIdx( 0 ); + size_t idx; + for( idx=0; idxsize(); idx++) + { + uint32 packedColor( 0 ); + if (c) + { + osg::Vec4 color = (*c)[ cIdx ]; + packedColor = (int)(color[3]*255) << 24 | + (int)(color[2]*255) << 16 | (int)(color[1]*255) << 8 | + (int)(color[0]*255); + } + else + osg::notify( osg::DEBUG_INFO ) << "fltexp: VPM::writeRecords: no color array." << std::endl; + + // Write fields common to all record types. + _vertices->writeInt16( opcode ); + _vertices->writeUInt16( sizeBytes ); + _vertices->writeUInt16( 0 ); // Color name + _vertices->writeInt16( flags ); // Flags + _vertices->writeVec3d( osg::Vec3d( (*v)[ idx ] ) ); // Vertex + + // Now write record-specific field. + switch( recType ) + { + case VERTEX_C: + _vertices->writeInt32( packedColor ); // Packed color + _vertices->writeUInt32( 0 ); // Vertex color index + break; + case VERTEX_CN: + _vertices->writeVec3f( (*n)[ nIdx ] ); // Normal + _vertices->writeInt32( packedColor ); // Packed color + _vertices->writeUInt32( 0 ); // Vertex color index + if (_fltOpt.getFlightFileVersionNumber() > ExportOptions::VERSION_15_7) + _vertices->writeUInt32( 0 ); // Reserved + break; + case VERTEX_CNT: + _vertices->writeVec3f( (*n)[ nIdx ] ); // Normal + _vertices->writeVec2f( (*t)[ idx ] ); // Tex coord + _vertices->writeInt32( packedColor ); // Packed color + _vertices->writeUInt32( 0 ); // Vertex color index + _vertices->writeUInt32( 0 ); // Reserved + break; + case VERTEX_CT: + _vertices->writeVec2f( (*t)[ idx ] ); // Tex coord + _vertices->writeInt32( packedColor ); // Packed color + _vertices->writeUInt32( 0 ); // Vertex color index + break; + } + + if (colorPerVertex) + cIdx++; + if (normalPerVertex) + nIdx++; + } +} + + + +VertexPaletteManager::ArrayInfo::ArrayInfo() + : _byteStart( 0 ), + _idxSizeBytes( 0 ), + _idxCount( 0 ) +{ +} + + +} diff --git a/src/osgPlugins/OpenFlight/VertexPaletteManager.h b/src/osgPlugins/OpenFlight/VertexPaletteManager.h new file mode 100644 index 000000000..822105759 --- /dev/null +++ b/src/osgPlugins/OpenFlight/VertexPaletteManager.h @@ -0,0 +1,99 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#ifndef __FLTEXP_VERTEX_PALETTE_MANAGER_H__ +#define __FLTEXP_VERTEX_PALETTE_MANAGER_H__ 1 + + +#include "DataOutputStream.h" +#include "ExportOptions.h" +#include +#include +#include + +namespace osg { + class Geometry; +} + + +namespace flt +{ + + +/*! + Manages writing the Vertex Palette record during export. + Maintains a map to ensure that instanced VertexArray data is only + written once to the palette. Writes the palette record to a temp + file and copies it to FltExportVisitor::_dos after the scene graph + has been completely walked. + */ +class VertexPaletteManager +{ +public: + VertexPaletteManager( const ExportOptions& fltOpt ); + ~VertexPaletteManager(); + + void add( const osg::Geometry& geom ); + void add( const osg::Vec3Array* v, const osg::Vec4Array* c, + const osg::Vec3Array* n, const osg::Vec2Array* t, + bool colorPerVertex, bool normalPerVertex, bool allowSharing=true ); + + unsigned int byteOffset( unsigned int idx ) const; + + void write( DataOutputStream& dos ) const; + +protected: + typedef enum { + VERTEX_C, + VERTEX_CN, + VERTEX_CNT, + VERTEX_CT + } PaletteRecordType; + + static PaletteRecordType recordType( const osg::Array* v, const osg::Array* c, + const osg::Array* n, const osg::Array* t ); + unsigned int recordSize( PaletteRecordType recType ); + + void writeRecords( const osg::Vec3Array* v, const osg::Vec4Array* c, + const osg::Vec3Array* n, const osg::Vec2Array* t, + bool colorPerVertex, bool normalPerVertex ); + + unsigned int _currentSizeBytes; + + struct ArrayInfo { + ArrayInfo(); + + unsigned int _byteStart; + unsigned int _idxSizeBytes; + unsigned int _idxCount; + }; + ArrayInfo* _current; + ArrayInfo _nonShared; + + typedef std::map< const osg::Array*, ArrayInfo > ArrayMap; + ArrayMap _arrayMap; + + mutable std::ofstream _verticesStr; + DataOutputStream* _vertices; + std::string _verticesTempName; + + const ExportOptions& _fltOpt; +}; + + +} + +#endif diff --git a/src/osgPlugins/OpenFlight/expAncillaryRecords.cpp b/src/osgPlugins/OpenFlight/expAncillaryRecords.cpp new file mode 100644 index 000000000..0b92eefb2 --- /dev/null +++ b/src/osgPlugins/OpenFlight/expAncillaryRecords.cpp @@ -0,0 +1,109 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "FltExportVisitor.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include +#include + + +namespace flt +{ + + +/** If the DataOutputStream paraqmeter is NULL, write to the _records + member variable. Otherwise, write to the specified DataOutputStream. + */ +void +FltExportVisitor::writeComment( const osg::Node& node, DataOutputStream* dos ) +{ + if (dos==NULL) + dos = _records; + + // Write all descriptions as Comment records. + unsigned int nd = node.getNumDescriptions(); + unsigned int idx=0; + while( idx < nd ) + { + const std::string& com = node.getDescription( idx ); + unsigned int iLen = com.length() + 5; + if (iLen > 0xffff) + { + // short overrun + std::string warning( "fltexp: writeComment: Descriptions too long, resorts in short overrun. Skipping." ); + _fltOpt->getWriteResult().warn( warning ); + osg::notify( osg::WARN ) << warning << std::endl; + continue; + } + uint16 length( (uint16)iLen ); + + dos->writeInt16( (int16) COMMENT_OP ); + dos->writeInt16( length ); + dos->writeString( com ); + + idx++; + } +} + +/** If the DataOutputStream paraqmeter is NULL, write to the _records + member variable. Otherwise, write to the specified DataOutputStream. + */ +void +FltExportVisitor::writeLongID( const std::string& id, DataOutputStream* dos ) +{ + if (dos==NULL) + dos = _records; + + uint16 length( 2 + 2 + id.length() + 1 ); // +1 for terminating '\0' + + dos->writeInt16( (int16) LONG_ID_OP ); + dos->writeUInt16( length ); + dos->writeString( id ); +} + +void +FltExportVisitor::writeMatrix( const osg::Referenced* ref ) +{ + const osg::RefMatrix* rm = dynamic_cast( ref ); + if (!rm) + return; + + uint16 length( 4 + (16 * sizeof(float32)) ); + + _records->writeInt16( (int16) MATRIX_OP ); + _records->writeUInt16( length ); + + int idx, jdx; + for (idx=0; idx<4; idx++) + { + for (jdx=0; jdx<4; jdx++) + { + _records->writeFloat32( (*rm)( idx, jdx ) ); + } + } +} + +void +FltExportVisitor::writeContinuationRecord( const unsigned short length ) +{ + osg::notify( osg::DEBUG_INFO ) << "fltexp: Continuation record length: " << length+4 << std::endl; + _records->writeInt16( (int16) CONTINUATION_OP ); + _records->writeUInt16( length+4 ); +} + + +} diff --git a/src/osgPlugins/OpenFlight/expControlRecords.cpp b/src/osgPlugins/OpenFlight/expControlRecords.cpp new file mode 100644 index 000000000..5b9106e96 --- /dev/null +++ b/src/osgPlugins/OpenFlight/expControlRecords.cpp @@ -0,0 +1,55 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "FltExportVisitor.h" +#include "DataOutputStream.h" +#include "Opcodes.h" + + +namespace flt +{ + + +void +FltExportVisitor::writePush() +{ + _records->writeInt16( (int16) PUSH_LEVEL_OP ); + _records->writeInt16( 4 ); +} + +void +FltExportVisitor::writePop() +{ + _records->writeInt16( (int16) POP_LEVEL_OP ); + _records->writeInt16( 4 ); +} + +void +FltExportVisitor::writePushSubface() +{ + _records->writeInt16( (int16) PUSH_SUBFACE_OP ); + _records->writeInt16( 4 ); +} + +void +FltExportVisitor::writePopSubface() +{ + _records->writeInt16( (int16) POP_SUBFACE_OP ); + _records->writeInt16( 4 ); +} + + +} diff --git a/src/osgPlugins/OpenFlight/expGeometryRecords.cpp b/src/osgPlugins/OpenFlight/expGeometryRecords.cpp new file mode 100644 index 000000000..3a1311033 --- /dev/null +++ b/src/osgPlugins/OpenFlight/expGeometryRecords.cpp @@ -0,0 +1,1106 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "FltExportVisitor.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include "MaterialPaletteManager.h" +#include "TexturePaletteManager.h" +#include "VertexPaletteManager.h" +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace flt +{ + + +// Bit flags for multitexturing +static unsigned int LAYER_1( 0x80000000 >> 0 ); +static unsigned int LAYER_2( 0x80000000 >> 1 ); +static unsigned int LAYER_3( 0x80000000 >> 2 ); +static unsigned int LAYER_4( 0x80000000 >> 3 ); +static unsigned int LAYER_5( 0x80000000 >> 4 ); +static unsigned int LAYER_6( 0x80000000 >> 5 ); +static unsigned int LAYER_7( 0x80000000 >> 6 ); + + + +bool +FltExportVisitor::isLit( const osg::Geometry& geom ) const +{ + const osg::StateSet* ss = getCurrentStateSet(); + if ( ss->getMode( GL_LIGHTING ) & osg::StateAttribute::ON ) + return true; + else + return false; //( geom.getNormalBinding() == osg::Geometry::BIND_PER_VERTEX ); +} + +bool +FltExportVisitor::isTextured( int unit, const osg::Geometry& geom ) const +{ + const osg::StateSet* ss = getCurrentStateSet(); + bool texOn( ss->getTextureMode( unit, GL_TEXTURE_2D ) & osg::StateAttribute::ON ); + bool hasCoords( geom.getTexCoordArray( unit ) != NULL ); + + return( texOn && hasCoords ); +} + +bool +FltExportVisitor::isAllMesh( const osg::Geometry& geom ) const +{ + // Return true if all primitive types will use Mesh records for output. + unsigned int jdx; + for (jdx=0; jdx < geom.getNumPrimitiveSets(); jdx++) + { + const osg::PrimitiveSet* prim = geom.getPrimitiveSet( jdx ); + if( (prim->getMode() != GL_TRIANGLE_STRIP) && + (prim->getMode() != GL_TRIANGLE_FAN) && + (prim->getMode() != GL_QUAD_STRIP) ) + return false; + } + return true; +} + +void +FltExportVisitor::writeFace( const osg::Geode& geode, const osg::Geometry& geom, GLenum mode ) +{ + enum DrawMode + { + SOLID_BACKFACE = 0, + SOLID_NO_BACKFACE = 1, + WIREFRAME_CLOSED = 2, + WIREFRAME_NOT_CLOSED = 3, + SURROUND_ALTERNATE_COLOR = 4, + OMNIDIRECTIONAL_LIGHT = 8, + UNIDIRECTIONAL_LIGHT = 9, + BIDIRECTIONAL_LIGHT = 10 + }; + enum TemplateMode + { + FIXED_NO_ALPHA_BLENDING = 0, + FIXED_ALPHA_BLENDING = 1, + AXIAL_ROTATE_WITH_ALPHA_BLENDING = 2, + POINT_ROTATE_WITH_ALPHA_BLENDING = 4 + }; + + const unsigned int TERRAIN_BIT = 0x80000000u >> 0; + const unsigned int NO_COLOR_BIT = 0x80000000u >> 1; + const unsigned int NO_ALT_COLOR_BIT = 0x80000000u >> 2; + const unsigned int PACKED_COLOR_BIT = 0x80000000u >> 3; + const unsigned int FOOTPRINT_BIT = 0x80000000u >> 4; // Terrain culture cutout + const unsigned int HIDDEN_BIT = 0x80000000u >> 5; + const unsigned int ROOFLINE_BIT = 0x80000000u >> 6; + uint32 flags( PACKED_COLOR_BIT ); + if (geode.getNodeMask() == 0) + flags |= HIDDEN_BIT; + + osg::StateSet const* ss = getCurrentStateSet(); + enum LightMode + { + FACE_COLOR = 0, + VERTEX_COLOR = 1, + FACE_COLOR_LIGHTING = 2, + VERTEX_COLOR_LIGHTING = 3 + }; + int8 lightMode; + osg::Vec4 packedColorRaw( 1., 1., 1., 1. ); + uint16 transparency( 0 ); + if (geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX) + { + if( isLit( geom ) ) + lightMode = VERTEX_COLOR_LIGHTING; + else + lightMode = VERTEX_COLOR; + } + else + { + const osg::Vec4Array* c = dynamic_cast( geom.getColorArray() ); + if (c && (c->size() > 0)) + { + packedColorRaw = (*c)[0]; + transparency = (1. - packedColorRaw[3]) * (double)0xffff; + } + + if ( isLit( geom ) ) + lightMode = FACE_COLOR_LIGHTING; + else + lightMode = FACE_COLOR; + } + uint32 packedColor; + packedColor = (int)(packedColorRaw[3]*255) << 24 | + (int)(packedColorRaw[2]*255) << 16 | (int)(packedColorRaw[1]*255) << 8 | + (int)(packedColorRaw[0]*255); + + + int8 drawType; + + switch( mode ) + { + case GL_POINTS: + { + std::string warning( "fltexp: GL_POINTS not supported in FLT export." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + break; + } + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUAD_STRIP: + { + std::string warning( "fltexp: Wrong mode in Face record." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + break; + } + case GL_LINES: + case GL_LINE_STRIP: + drawType = WIREFRAME_NOT_CLOSED; + break; + case GL_LINE_LOOP: + drawType = WIREFRAME_CLOSED; + break; + case GL_TRIANGLES: + case GL_QUADS: + case GL_POLYGON: + { + // Default to no facet culling + drawType = SOLID_NO_BACKFACE; + + // If facet-culling isn't *dis*abled, check whether the CullFace mode is BACK + if (ss->getMode(GL_CULL_FACE) & osg::StateAttribute::ON) + { + osg::CullFace const* cullFace = static_cast( + ss->getAttribute(osg::StateAttribute::CULLFACE) ); + if( cullFace->getMode() == osg::CullFace::BACK ) + drawType = SOLID_BACKFACE; + + // Note: OpenFlt can't handle FRONT or FRONT_AND_BACK settings, so ignore these(??) + } + break; + } + } + + // Determine the material properties for the face + int16 materialIndex( -1 ); + if (isLit( geom )) + { + osg::Material const* currMaterial = static_cast( + ss->getAttribute(osg::StateAttribute::MATERIAL) ); + materialIndex = _materialPalette->add(currMaterial); + } + + // Get base texture + int16 textureIndex( -1 ); + if (isTextured( 0, geom )) + { + const osg::Texture2D* texture = static_cast( + ss->getTextureAttribute( 0, osg::StateAttribute::TEXTURE ) ); + if (texture != NULL) + textureIndex = _texturePalette->add( 0, texture ); + else + { + std::string warning( "fltexp: Face is textured, but Texture2D StateAttribute is NULL." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + } + } + + // Check for blending. We're not a Billboard (TBD?) + // so use either FIXED_NO_ALPHA_BLENDING or FIXED_ALPHA_BLENDING. + TemplateMode templateMode( FIXED_NO_ALPHA_BLENDING ); + if ( ss->getMode( GL_BLEND ) & osg::StateAttribute::ON ) + { + const osg::BlendFunc* bf = static_cast( + ss->getAttribute(osg::StateAttribute::BLENDFUNC) ); + if( (bf->getSource() == osg::BlendFunc::SRC_ALPHA) && + (bf->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA) ) + templateMode = FIXED_ALPHA_BLENDING; + } + + + uint16 length( 80 ); + IdHelper id( *this, geode.getName() ); + + _records->writeInt16( (int16) FACE_OP ); + _records->writeUInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // IR color code + _records->writeInt16( 0 ); // Relative priority + _records->writeInt8( drawType ); // Draw type + _records->writeInt8( 0 ); // Texture white + _records->writeInt16( -1 ); // Color name index + _records->writeInt16( -1 ); // Alternate color name index + _records->writeInt8( 0 ); // Reserved + _records->writeInt8( templateMode ); // Template (billboard) + _records->writeInt16( -1 ); // Detail texture pattern index + _records->writeInt16( textureIndex ); // Texture pattern index + _records->writeInt16( materialIndex ); // Material index + _records->writeInt16( 0 ); // Surface material code + _records->writeInt16( 0 ); // Feature ID + _records->writeInt32( 0 ); // IR material code + _records->writeUInt16( transparency ); // Transparency + _records->writeInt8( 0 ); // LOD generation control + _records->writeInt8( 0 ); // Line style index + _records->writeUInt32( flags ); // Flags + _records->writeInt8( lightMode ); // Light mode + _records->writeFill( 7 ); // Reserved + _records->writeUInt32( packedColor ); // Packed color, primary + _records->writeUInt32( 0x00ffffff ); // Packed color, alternate + _records->writeInt16( -1 ); // Texture mapping index + _records->writeInt16( 0 ); // Reserved + _records->writeInt32( -1 ); // Primary color index + _records->writeInt32( -1 ); // Alternate color index + // Next four bytes: + // 15.8: two 2-byte "reserved" fields + // 15.9: one 4-byte "reserved" field + _records->writeInt16( 0 ); // Reserved + _records->writeInt16( -1 ); // Shader index +} + + +void +FltExportVisitor::writeMesh( const osg::Geode& geode, const osg::Geometry& geom, GLenum mode ) +{ + enum DrawMode + { + SOLID_BACKFACE = 0, + SOLID_NO_BACKFACE = 1, + WIREFRAME_CLOSED = 2, + WIREFRAME_NOT_CLOSED = 3, + SURROUND_ALTERNATE_COLOR = 4, + OMNIDIRECTIONAL_LIGHT = 8, + UNIDIRECTIONAL_LIGHT = 9, + BIDIRECTIONAL_LIGHT = 10 + }; + enum TemplateMode + { + FIXED_NO_ALPHA_BLENDING = 0, + FIXED_ALPHA_BLENDING = 1, + AXIAL_ROTATE_WITH_ALPHA_BLENDING = 2, + POINT_ROTATE_WITH_ALPHA_BLENDING = 4 + }; + + const unsigned int TERRAIN_BIT = 0x80000000u >> 0; + const unsigned int NO_COLOR_BIT = 0x80000000u >> 1; + const unsigned int NO_ALT_COLOR_BIT = 0x80000000u >> 2; + const unsigned int PACKED_COLOR_BIT = 0x80000000u >> 3; + const unsigned int FOOTPRINT_BIT = 0x80000000u >> 4; // Terrain culture cutout + const unsigned int HIDDEN_BIT = 0x80000000u >> 5; + const unsigned int ROOFLINE_BIT = 0x80000000u >> 6; + uint32 flags( PACKED_COLOR_BIT ); + if (geode.getNodeMask() == 0) + flags |= HIDDEN_BIT; + + enum LightMode + { + FACE_COLOR = 0, + VERTEX_COLOR = 1, + FACE_COLOR_LIGHTING = 2, + VERTEX_COLOR_LIGHTING = 3 + }; + int8 lightMode; + osg::Vec4 packedColorRaw( 1., 1., 1., 1. ); + uint16 transparency( 0 ); + if (geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX) + { + if (isLit( geom )) + lightMode = VERTEX_COLOR_LIGHTING; + else + lightMode = VERTEX_COLOR; + } + else + { + const osg::Vec4Array* c = dynamic_cast( geom.getColorArray() ); + if (c && (c->size() > 0)) + { + packedColorRaw = (*c)[0]; + transparency = (1. - packedColorRaw[3]) * (double)0xffff; + } + + if (isLit( geom )) + lightMode = FACE_COLOR_LIGHTING; + else + lightMode = FACE_COLOR; + } + uint32 packedColor; + packedColor = (int)(packedColorRaw[3]*255) << 24 | + (int)(packedColorRaw[2]*255) << 16 | (int)(packedColorRaw[1]*255) << 8 | + (int)(packedColorRaw[0]*255); + + + int8 drawType; + osg::StateSet const* ss = getCurrentStateSet(); + + switch( mode ) + { + case GL_POINTS: + { + std::string warning( "fltexp: GL_POINTS not supported in FLT export." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + break; + } + case GL_LINES: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_TRIANGLES: + case GL_QUADS: + { + std::string warning( "fltexp: Wrong mode in Mesh record." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + break; + } + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUAD_STRIP: + case GL_POLYGON: + { + // Default to no facet culling + drawType = SOLID_NO_BACKFACE; + + // If facet-culling isn't *dis*abled, check whether the CullFace mode is BACK + if (ss->getMode(GL_CULL_FACE) & osg::StateAttribute::ON) + { + osg::CullFace const* cullFace = static_cast( + ss->getAttribute(osg::StateAttribute::CULLFACE) ); + if( cullFace->getMode() == osg::CullFace::BACK ) + drawType = SOLID_BACKFACE; + + // Note: OpenFlt can't handle FRONT or FRONT_AND_BACK settings, so ignore these(??) + } + break; + } + } + + // Determine the material properties for the face + int16 materialIndex( -1 ); + if (isLit( geom )) + { + osg::Material const* currMaterial = static_cast( + ss->getAttribute(osg::StateAttribute::MATERIAL) ); + materialIndex = _materialPalette->add(currMaterial); + } + + // Get base texture + int16 textureIndex( -1 ); + if (isTextured( 0, geom )) + { + const osg::Texture2D* texture = static_cast( + ss->getTextureAttribute( 0, osg::StateAttribute::TEXTURE ) ); + if (texture != NULL) + textureIndex = _texturePalette->add( 0, texture ); + else + { + std::string warning( "fltexp: Mesh is textured, but Texture2D StateAttribute is NULL." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + } + } + + // Check for blending. We're not a Billboard (TBD?) + // so use either FIXED_NO_ALPHA_BLENDING or FIXED_ALPHA_BLENDING. + TemplateMode templateMode( FIXED_NO_ALPHA_BLENDING ); + if ( ss->getMode( GL_BLEND ) & osg::StateAttribute::ON ) + { + const osg::BlendFunc* bf = static_cast( + ss->getAttribute(osg::StateAttribute::BLENDFUNC) ); + if( (bf->getSource() == osg::BlendFunc::SRC_ALPHA) && + (bf->getDestination() == osg::BlendFunc::ONE_MINUS_SRC_ALPHA) ) + templateMode = FIXED_ALPHA_BLENDING; + } + + + uint16 length( 84 ); + IdHelper id( *this, geode.getName() ); + + _records->writeInt16( (int16) MESH_OP ); + _records->writeUInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // Reserved + _records->writeInt32( 0 ); // IR color code + _records->writeInt16( 0 ); // Relative priority + _records->writeInt8( drawType ); // Draw type + _records->writeInt8( 0 ); // Texture white + _records->writeInt16( -1 ); // Color name index + _records->writeInt16( -1 ); // Alternate color name index + _records->writeInt8( 0 ); // Reserved + _records->writeInt8( templateMode ); // Template (billboard) + _records->writeInt16( -1 ); // Detail texture pattern index + _records->writeInt16( textureIndex ); // Texture pattern index + _records->writeInt16( materialIndex ); // Material index + _records->writeInt16( 0 ); // Surface material code + _records->writeInt16( 0 ); // Feature ID + _records->writeInt32( 0 ); // IR material code + _records->writeUInt16( transparency ); // Transparency + _records->writeInt8( 0 ); // LOD generation control + _records->writeInt8( 0 ); // Line style index + _records->writeUInt32( flags ); // Flags + _records->writeInt8( lightMode ); // Light mode + _records->writeFill( 7 ); // Reserved + _records->writeUInt32( packedColor ); // Packed color, primary + _records->writeUInt32( 0x00ffffff ); // Packed color, alternate + _records->writeInt16( -1 ); // Texture mapping index + _records->writeInt16( 0 ); // Reserved + _records->writeInt32( -1 ); // Primary color index + _records->writeInt32( -1 ); // Alternate color index + // Next four bytes: + // 15.8: two 2-byte "reserved" fields + // 15.9: one 4-byte "reserved" field + _records->writeInt16( 0 ); // Reserved + _records->writeInt16( -1 ); // Shader index +} + +int +FltExportVisitor::writeVertexList( int first, unsigned int count ) +{ + _records->writeInt16( (int16) VERTEX_LIST_OP ); + _records->writeUInt16( 4 + (count*4) ); + + unsigned int idx; + for( idx=0; idxwriteInt32( _vertexPalette->byteOffset( first+idx ) ); + + return count; +} + +int +FltExportVisitor::writeVertexList( const std::vector& indices, unsigned int count ) +{ + _records->writeInt16( (int16) VERTEX_LIST_OP ); + _records->writeUInt16( 4 + (count*4) ); + + unsigned int idx; + for( idx=0; idxwriteInt32( _vertexPalette->byteOffset( indices[ idx ] ) ); + + return count; +} + +void +FltExportVisitor::writeMeshPrimitive( const std::vector& indices, GLenum mode ) +{ + int16 primType; + switch( mode ) + { + case GL_TRIANGLE_STRIP: + primType = 1; + break; + case GL_TRIANGLE_FAN: + primType = 2; + break; + case GL_QUAD_STRIP: + primType = 3; + break; + default: + // Warning should already be recorded. Do nothing. + return; + break; + } + + uint16 length( 12 + (4 * indices.size()) ); + + _records->writeInt16( (int16) MESH_PRIMITIVE_OP ); + _records->writeUInt16( length ); + _records->writeInt16( primType ); // primitive type + _records->writeInt16( 4 ); // index size, 4 bytes + _records->writeInt32( indices.size() ); // vertex count + + std::vector::const_iterator it = indices.begin(); + while (it != indices.end()) + { + _records->writeUInt32( (*it) ); + it++; + } +} + +void +FltExportVisitor::writeLocalVertexPool( const osg::Geometry& geom, GLenum mode ) +{ + // Attribute Mask + static const unsigned int HAS_POSITION = 0x80000000u >> 0; + static const unsigned int HAS_COLOR_INDEX = 0x80000000u >> 1; + static const unsigned int HAS_RGBA_COLOR = 0x80000000u >> 2; + static const unsigned int HAS_NORMAL = 0x80000000u >> 3; + static const unsigned int HAS_BASE_UV = 0x80000000u >> 4; + static const unsigned int HAS_UV_LAYER1 = 0x80000000u >> 5; + static const unsigned int HAS_UV_LAYER2 = 0x80000000u >> 6; + static const unsigned int HAS_UV_LAYER3 = 0x80000000u >> 7; + static const unsigned int HAS_UV_LAYER4 = 0x80000000u >> 8; + static const unsigned int HAS_UV_LAYER5 = 0x80000000u >> 9; + static const unsigned int HAS_UV_LAYER6 = 0x80000000u >> 10; + static const unsigned int HAS_UV_LAYER7 = 0x80000000u >> 11; + + const osg::Array* v = geom.getVertexArray(); + uint32 numVerts( v->getNumElements() ); + const osg::Vec3Array* v3 = dynamic_cast( v ); + if (!v3) + { + std::string warning( "fltexp: writeLocalVertexPool: VertexArray is not Vec3Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } + + // Compute attribute bits and vertex size. + const osg::Array* c = geom.getColorArray(); + const osg::Array* n = geom.getNormalArray(); + const osg::Array* t = geom.getTexCoordArray( 0 ); + + std::vector< const osg::Vec2Array* > mtc; + mtc.resize( 8 ); + int unit=1; + for( ;unit<8; unit++) + mtc[ unit ] = dynamic_cast( geom.getTexCoordArray( unit ) ); + + const osg::Vec4Array* c4 = dynamic_cast( c ); + const osg::Vec3Array* n3 = dynamic_cast( n ); + const osg::Vec2Array* t2 = dynamic_cast( t ); + if (c && !c4) + { + std::string warning( "fltexp: writeLocalVertexPool: ColorArray is not Vec4Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } + if (n && !n3) + { + std::string warning( "fltexp: writeLocalVertexPool: NormalArray is not Vec3Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } + if (t && !t2) + { + std::string warning( "fltexp: writeLocalVertexPool: TexCoordArray is not Vec2Array." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } + + uint32 attr( HAS_POSITION ); + unsigned int vertSize( sizeof( float64 ) * 3 ); + + if (c4 && ( geom.getColorBinding() == osg::Geometry::BIND_PER_VERTEX) ) + { + attr |= HAS_RGBA_COLOR; + vertSize += sizeof( unsigned int ); + } + if (n3 && ( geom.getNormalBinding() == osg::Geometry::BIND_PER_VERTEX) ) + { + attr |= HAS_NORMAL; + vertSize += ( sizeof( float32 ) * 3 ); + } + if (t2) + { + attr |= HAS_BASE_UV; + vertSize += ( sizeof( float32 ) * 2 ); + } + // Add multitex + if (isTextured( 1, geom )) + { + attr |= HAS_UV_LAYER1; + vertSize += ( sizeof( float32 ) * 2 ); + } + if (isTextured( 2, geom )) + { + attr |= HAS_UV_LAYER2; + vertSize += ( sizeof( float32 ) * 2 ); + } + if (isTextured( 3, geom )) + { + attr |= HAS_UV_LAYER3; + vertSize += ( sizeof( float32 ) * 2 ); + } + if (isTextured( 4, geom )) + { + attr |= HAS_UV_LAYER4; + vertSize += ( sizeof( float32 ) * 2 ); + } + if (isTextured( 5, geom )) + { + attr |= HAS_UV_LAYER5; + vertSize += ( sizeof( float32 ) * 2 ); + } + if (isTextured( 6, geom )) + { + attr |= HAS_UV_LAYER6; + vertSize += ( sizeof( float32 ) * 2 ); + } + if (isTextured( 7, geom )) + { + attr |= HAS_UV_LAYER7; + vertSize += ( sizeof( float32 ) * 2 ); + } + + unsigned int maxVerts = (0xffff - 12) / vertSize; + unsigned int thisVertCount = (maxVerts > numVerts) ? numVerts : maxVerts; + unsigned int currentIndexLimit = maxVerts; + uint16 length( 12 + (vertSize * thisVertCount) ); + + + _records->writeInt16( (int16) LOCAL_VERTEX_POOL_OP ); + _records->writeUInt16( length ); + _records->writeUInt32( numVerts ); // number of vertices + _records->writeUInt32( attr ); // attribute bits + + unsigned int idx; + for( idx=0; idxwriteFloat64( (*v3)[ idx ][0] ); + _records->writeFloat64( (*v3)[ idx ][1] ); + _records->writeFloat64( (*v3)[ idx ][2] ); + + if (attr & HAS_RGBA_COLOR) + { + osg::Vec4 color = (*c4)[ idx ]; + unsigned int packedColor = (int)(color[3]*255) << 24 | + (int)(color[2]*255) << 16 | (int)(color[1]*255) << 8 | + (int)(color[0]*255); + _records->writeUInt32( packedColor ); + } + + if (attr & HAS_NORMAL) + _records->writeVec3f( (*n3)[ idx ] ); + + if (attr & HAS_BASE_UV) + _records->writeVec2f( (*t2)[ idx ] ); + + if (attr & HAS_UV_LAYER1) + _records->writeVec2f( (*mtc[1])[ idx ] ); + if (attr & HAS_UV_LAYER2) + _records->writeVec2f( (*mtc[2])[ idx ] ); + if (attr & HAS_UV_LAYER3) + _records->writeVec2f( (*mtc[3])[ idx ] ); + if (attr & HAS_UV_LAYER4) + _records->writeVec2f( (*mtc[4])[ idx ] ); + if (attr & HAS_UV_LAYER5) + _records->writeVec2f( (*mtc[5])[ idx ] ); + if (attr & HAS_UV_LAYER6) + _records->writeVec2f( (*mtc[6])[ idx ] ); + if (attr & HAS_UV_LAYER7) + _records->writeVec2f( (*mtc[7])[ idx ] ); + + + // Handle continuation record if necessary. + if ( (idx+1 == currentIndexLimit) && (idx+1 < numVerts) ) + { + currentIndexLimit += maxVerts; + unsigned int remaining( numVerts - (idx+1) ); + thisVertCount = (maxVerts > remaining) ? remaining : maxVerts; + writeContinuationRecord( (vertSize * thisVertCount) ); + } + } +} + +void +FltExportVisitor::writeMultitexture( const osg::Geometry& geom ) +{ + unsigned int numLayers( 0 ); + uint32 flags( 0 ); + unsigned int idx; + for( idx=1; idx<8; idx++) + { + if( isTextured( idx, geom ) ) + { + flags |= LAYER_1 >> (idx-1); + numLayers++; + } + } + if( numLayers == 0 ) + return; + + uint16 length( 8 + (8*numLayers) ); + + _records->writeInt16( (int16) MULTITEXTURE_OP ); + _records->writeUInt16( length ); + _records->writeInt32( flags ); + + const osg::StateSet* ss = getCurrentStateSet(); + for( idx=1; idx<8; idx++) + { + if( isTextured( idx, geom ) ) + { + int16 textureIndex( -1 ); + const osg::Texture2D* texture = static_cast( + ss->getTextureAttribute( idx, osg::StateAttribute::TEXTURE ) ); + if (texture != NULL) + textureIndex = _texturePalette->add( idx, texture ); + else + { + std::ostringstream warning; + warning << "fltexp: No Texture2D for unit " << idx; + osg::notify( osg::WARN ) << warning.str() << std::endl; + _fltOpt->getWriteResult().warn( warning.str() ); + } + + _records->writeUInt16( textureIndex ); // texture index + _records->writeUInt16( 0 ); // TBD effect + // mapping index (this value is an unsigned int, but has a -1 default per oflt spec: ugh) + _records->writeUInt16( static_cast( -1 ) ); + _records->writeUInt16( 0 ); // data + } + } +} + +void +FltExportVisitor::writeUVList( int numVerts, const osg::Geometry& geom ) +{ + unsigned int numLayers( 0 ); + uint32 flags( 0 ); + unsigned int idx; + for( idx=1; idx<8; idx++) + { + if( isTextured( idx, geom ) ) + { + flags |= LAYER_1 >> (idx-1); + numLayers++; + } + } + if( numLayers == 0 ) + return; + + uint16 length( 8 + (8*numLayers*numVerts) ); + + _records->writeInt16( (int16) UV_LIST_OP ); + _records->writeUInt16( length ); + _records->writeInt32( flags ); + + osg::Vec2 defaultCoord( 0., 0. ); + const osg::StateSet* ss = getCurrentStateSet(); + for( idx=1; idx<8; idx++) + { + if( isTextured( idx, geom ) ) + { + osg::Array* t = const_cast( geom.getTexCoordArray( idx ) ); + osg::ref_ptr t2 = dynamic_cast( t ); + if (!t2.valid()) + { + std::ostringstream warning; + warning << "fltexp: No Texture2D for unit " << idx; + osg::notify( osg::WARN ) << warning.str() << std::endl; + _fltOpt->getWriteResult().warn( warning.str() ); + t2 = new osg::Vec2Array; + } + else if (t2->getNumElements() != numVerts) + { + std::ostringstream warning; + warning << "fltexp: Invalid number of texture coordinates for unit " << idx; + osg::notify( osg::WARN ) << warning.str() << std::endl; + _fltOpt->getWriteResult().warn( warning.str() ); + } + + const int size = t2->getNumElements(); + int vIdx; + for( vIdx=0; vIdxwriteFloat32( tc[0] ); + _records->writeFloat32( tc[1] ); + } + } + } +} + + + +void +FltExportVisitor::handleDrawArrays( const osg::DrawArrays* da, const osg::Geometry& geom, const osg::Geode& geode ) +{ + GLint first = da->getFirst(); + GLsizei count = da->getCount(); + GLenum mode = da->getMode(); + + int n( 0 ); + bool useMesh( false ); + switch( mode ) + { + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUAD_STRIP: + useMesh = true; + break; + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_POLYGON: + default: + n = count; + break; + case GL_LINES: + n = 2; + break; + case GL_TRIANGLES: + n = 3; + break; + case GL_QUADS: + n = 4; + break; + } + + // Push and pop subfaces if polygon offset is on. + SubfaceHelper subface( *this, getCurrentStateSet() ); + + if (useMesh) + { + writeMesh( geode, geom, mode ); + + writeMatrix( geode.getUserData() ); + writeComment( geode ); + writeMultitexture( geom ); + writeLocalVertexPool( geom, mode ); + + writePush(); + + std::vector< unsigned int > indices; + int jdx; + for (jdx=0; jdxgetCount()) + { + // Need: + // * Geode for record name (but also need to handle + // multi Geometry objects and multi PrimitiveSet objects; + // all Face records can't have the same name). + // * Mode + writeFace( geode, geom, mode ); + + writeMatrix( geode.getUserData() ); + writeComment( geode ); + writeMultitexture( geom ); + writePush(); + + // Write vertex list records. + int numVerts = writeVertexList( first, n ); + first += n; + + writeUVList( numVerts, geom ); + + writePop(); + } + } +} + +void +FltExportVisitor::handleDrawArrayLengths( const osg::DrawArrayLengths* dal, const osg::Geometry& geom, const osg::Geode& geode ) +{ + GLint first = dal->getFirst(); + GLenum mode = dal->getMode(); + + int n( 0 ); + bool useMesh( false ); + switch( mode ) + { + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUAD_STRIP: + useMesh = true; + break; + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_POLYGON: + default: + break; + case GL_LINES: + n = 2; + break; + case GL_TRIANGLES: + n = 3; + break; + case GL_QUADS: + n = 4; + break; + } + + // Push and pop subfaces if polygon offset is on. + SubfaceHelper subface( *this, getCurrentStateSet() ); + + if (useMesh) + { + writeMesh( geode, geom, mode ); + + writeMatrix( geode.getUserData() ); + writeComment( geode ); + writeMultitexture( geom ); + writeLocalVertexPool( geom, mode ); + + writePush(); + + int idx( 0 ); + for( osg::DrawArrayLengths::const_iterator itr=dal->begin(); + itr!=dal->end(); itr++ ) + { + std::vector< unsigned int > indices; + int jdx; + for (jdx=0; jdx<(*itr); idx++, jdx++) + indices.push_back( idx ); + writeMeshPrimitive( indices, mode ); + } + + writePop(); + } + else + { + // Hm. You wouldn't usually use DrawArrayLengths for non-strip/fan prims... + for( osg::DrawArrayLengths::const_iterator itr=dal->begin(); + itr!=dal->end(); itr++ ) + { + while (first+n <= *itr) + { + writeFace( geode, geom, mode ); + + writeMatrix( geode.getUserData() ); + writeComment( geode ); + writeMultitexture( geom ); + writePush(); + + // Write vertex list records. + int numVerts; + if (n == 0) + { + numVerts = writeVertexList( first, *itr ); + first += *itr; + } + else + { + numVerts = writeVertexList( first, n ); + first += n; + } + + writeUVList( numVerts, geom ); + + writePop(); + } + + first += *itr; + } + } +} + +void +FltExportVisitor::handleDrawElements( const osg::DrawElements* de, const osg::Geometry& geom, const osg::Geode& geode ) +{ + GLenum mode = de->getMode(); + + int n( 0 ); + bool useMesh( false ); + switch( mode ) + { + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUAD_STRIP: + n = de->getNumIndices(); + useMesh = true; + break; + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_POLYGON: + default: + n = de->getNumIndices(); + break; + case GL_LINES: + n = 2; + break; + case GL_TRIANGLES: + n = 3; + break; + case GL_QUADS: + n = 4; + break; + } + + // Push and pop subfaces if polygon offset is on. + SubfaceHelper subface( *this, getCurrentStateSet() ); + + if (useMesh) + { + writeMesh( geode, geom, mode ); + + writeMatrix( geode.getUserData() ); + writeComment( geode ); + writeMultitexture( geom ); + writeLocalVertexPool( geom, mode ); + + writePush(); + + std::vector< unsigned int > indices; + int idx; + for (idx=0; idxindex( idx ) ); + writeMeshPrimitive( indices, mode ); + + writePop(); + } + else + { + unsigned int first( 0 ); + while (first+n <= de->getNumIndices()) + { + // Need: + // * Geode for record name (but also need to handle + // multi Geometry objects and multi PrimitiveSet objects; + // all Face records can't have the same name). + // * Mode + writeFace( geode, geom, mode ); + + writeMatrix( geode.getUserData() ); + writeComment( geode ); + writeMultitexture( geom ); + writePush(); + + // Write vertex list records. + std::vector indices; + int idx; + for(idx=0; idxindex( first+idx ) ); + int numVerts = writeVertexList( indices, n ); + first += n; + + writeUVList( numVerts, geom ); + + writePop(); + } + } +} + + +} diff --git a/src/osgPlugins/OpenFlight/expPrimaryRecords.cpp b/src/osgPlugins/OpenFlight/expPrimaryRecords.cpp new file mode 100644 index 000000000..4fddee939 --- /dev/null +++ b/src/osgPlugins/OpenFlight/expPrimaryRecords.cpp @@ -0,0 +1,792 @@ +/* + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or (at + * your option) any later version. The full license is in the LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +// +// Copyright(c) 2008 Skew Matrix Software LLC. +// + +#include "FltExportVisitor.h" +#include "ExportOptions.h" +#include "VertexPaletteManager.h" +#include "LightSourcePaletteManager.h" +#include "DataOutputStream.h" +#include "Opcodes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// FIXME: This header was copied verbatim from the importer, with the only change +// being that the symbols it defines are placed in namespace 'fltexp' instead +// of 'flt'. Thus, this one-off copy has to be kept in sync with the +// importer until the reader/writer are unified... +#include "Pools.h" + + +namespace flt +{ + + +void +FltExportVisitor::writeHeader( const std::string& headerName ) +{ + int16 length; + int32 version; + const int ver = _fltOpt->getFlightFileVersionNumber(); + if (ver == ExportOptions::VERSION_15_7) + { + length = 304; + version = 1570; + } + else if (ver == ExportOptions::VERSION_15_8) + { + length = 324; + version = 1580; + } + else // ExportOptions::VERSION_16_1: + { + length = 324; + version = 1610; + } + + int8 units; + switch( _fltOpt->getFlightUnits() ) + { + case ExportOptions::KILOMETERS: + units = 1; + break; + case ExportOptions::FEET: + units = 4; + break; + case ExportOptions::INCHES: + units = 5; + break; + case ExportOptions::NAUTICAL_MILES: + units = 8; + break; + default: + case ExportOptions::METERS: + units = 0; + break; + } + + static const unsigned int SAVE_VERTEX_NORMALS_BIT = 0x80000000u >> 0; + static const unsigned int PACKED_COLOR_MODE_BIT = 0x80000000u >> 1; + static const unsigned int CAD_VIEW_MODE_BIT = 0x80000000u >> 2; + uint32 flags( SAVE_VERTEX_NORMALS_BIT ); + + IdHelper id(*this, headerName); + id.dos_ = &_dos; + + _dos.writeInt16( (int16) HEADER_OP ); + _dos.writeInt16( length ); + _dos.writeID( id ); + _dos.writeInt32( version ); + _dos.writeInt32( 0 ); // edit revision + // TBD need platform-independent method to generate date/time string + _dos.writeString( std::string(" "), 32 ); // date and time string for last rev + _dos.writeInt16( 0 ); // next group id + _dos.writeInt16( 0 ); // next LOD id + _dos.writeInt16( 0 ); // next object id + _dos.writeInt16( 0 ); // next face id + _dos.writeInt16( 1 ); // unit multiplier + _dos.writeInt8( units ); // coordinate units + _dos.writeInt8( 0 ); // if TRUE, texwhite on new faces + _dos.writeUInt32( flags ); // flags + _dos.writeFill( sizeof( int32 ) * 6 ); // reserved + _dos.writeInt32( 0 ); // projection + _dos.writeFill( sizeof( int32 ) * 7 ); // reserved + _dos.writeInt16( 0 ); // next DOF id + _dos.writeInt16( 1 ); // vertex storage type, should always be 1 + _dos.writeInt32( 100 ); // DB origin, 100=OpenFlight + _dos.writeFloat64( 0. ); // SW corner X + _dos.writeFloat64( 0. ); // SW corner Y + _dos.writeFloat64( 0. ); // delta X + _dos.writeFloat64( 0. ); // delta Y + _dos.writeInt16( 0 ); // next sound id + _dos.writeInt16( 0 ); // next path id + _dos.writeFill( sizeof( int32 ) * 2 ); // reserved + _dos.writeInt16( 0 ); // next clip id + _dos.writeInt16( 0 ); // next text id + _dos.writeInt16( 0 ); // next BSP id + _dos.writeInt16( 0 ); // next switch id + _dos.writeInt32( 0 ); // reserved + _dos.writeFloat64( 0. ); // SW corner lat + _dos.writeFloat64( 0. ); // SW corner lon + _dos.writeFloat64( 0. ); // NE corner lat + _dos.writeFloat64( 0. ); // NE corner lon + _dos.writeFloat64( 0. ); // origin lat + _dos.writeFloat64( 0. ); // origin lon + _dos.writeFloat64( 0. ); // lambert upper lat + _dos.writeFloat64( 0. ); // lambert upper lon + _dos.writeInt16( 0 ); // next light source id + _dos.writeInt16( 0 ); // next light point id + _dos.writeInt16( 0 ); // next road id + _dos.writeInt16( 0 ); // next CAT id + _dos.writeFill( sizeof( int16 ) * 4 ); // reserved + _dos.writeInt32( 0 ); // ellipsoid model, 0=WGS84 + _dos.writeInt16( 0 ); // next adaptive id + _dos.writeInt16( 0 ); // next curve id + _dos.writeInt16( 0 ); // utm zone + _dos.writeFill( 6 ); // reserved + _dos.writeFloat64( 0. ); // delta z + _dos.writeFloat64( 0. ); // radius + _dos.writeInt16( 0 ); // next mesh id + _dos.writeInt16( 0 ); // next light system id + + if (version >= 1580) + { + _dos.writeInt32( 0 ); // reserved + _dos.writeFloat64( 0. ); // earth major axis for user defined ellipsoid + _dos.writeFloat64( 0. ); // earth minor axis for user defined ellipsoid + } +} + + +// Group flags +static const unsigned int FORWARD_ANIM = 0x80000000u >> 1; +static const unsigned int SWING_ANIM = 0x80000000u >> 2; +static const unsigned int BOUND_BOX_FOLLOW = 0x80000000u >> 3; +static const unsigned int FREEZE_BOUND_BOX = 0x80000000u >> 4; +static const unsigned int DEFAULT_PARENT = 0x80000000u >> 5; +static const unsigned int BACKWARD_ANIM = 0x80000000u >> 6; + + +// +// Convenience routine for writing Group nodes that aren't animated +// +void +FltExportVisitor::writeGroup( const osg::Group& group ) +{ + int32 flags = 0, loopCount = 0; + float32 loopDuration = 0.0f, lastFrameDuration = 0.0f; + + writeGroup(group, flags, loopCount, loopDuration, lastFrameDuration); +} + + + +void +FltExportVisitor::writeGroup( const osg::Group& group, + int32 flags, + int32 loopCount, + float32 loopDuration, + float32 lastFrameDuration) // <-- placeholder: ignored +{ + int16 length( 44 ); + IdHelper id(*this, group.getName() ); + + _records->writeInt16( (int16) GROUP_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt16( 0 ); // Relative priority + _records->writeInt16( 0 ); // Reserved + _records->writeUInt32( flags ); + _records->writeInt16( 0 ); // Special effect ID1 + _records->writeInt16( 0 ); // Special effect ID2 + _records->writeInt16( 0 ); // Significance + _records->writeInt8( 0 ); // Layer code + _records->writeInt8( 0 ); // Reserved + _records->writeInt32( 0 ); // Reserved + _records->writeInt32( loopCount ); + _records->writeFloat32( loopDuration ); + _records->writeFloat32( lastFrameDuration ); +} + + +// +// Since OpenFlight doesn't have 'Sequence' records---just Group records that +// may, optionally, be animated---this routine sets the animation-related +// parameters for a Group record and simply forwards to writeGroup() +// +void +FltExportVisitor::writeSequence( const osg::Sequence& sequence ) +{ + + int32 flags = 0, loopCount = 0; + float32 loopDuration = 0.0f, lastFrameDuration = 0.0f; + + osg::Sequence::LoopMode mode; + int firstChildDisplayed, lastChildDisplayed; + sequence.getInterval(mode, firstChildDisplayed, lastChildDisplayed); + + if (firstChildDisplayed == 0) + { + flags |= FORWARD_ANIM; + } + + else + { + flags &= ~FORWARD_ANIM; + } + + if (mode == osg::Sequence::SWING) + { + flags |= SWING_ANIM; + } + + else + { + flags &= ~SWING_ANIM; + } + + // Do we loop infinitely, or only a certain number of times? + float speedUp; + int numReps; + sequence.getDuration(speedUp, numReps); + + if (numReps != -1) + { + loopCount = numReps; + } + + else + { + loopCount = 0; // == loop continuously + } + + // Sum individual frame durations to get the total loopDuration + for (unsigned int i = 0; i < sequence.getNumChildren(); ++i) + { + loopDuration += sequence.getTime(i); + } + + lastFrameDuration = sequence.getLastFrameTime(); + + writeGroup(sequence, flags, loopCount, loopDuration, lastFrameDuration); +} + + +void +FltExportVisitor::writeObject( const osg::Group& group, osgSim::ObjectRecordData* ord ) +{ + uint16 length( 28 ); + IdHelper id(*this, group.getName() ); + + if (!ord) + { + std::string warning( "fltexp: writeObject has invalid ObjectRecordData." ); + osg::notify( osg::WARN ) << warning << std::endl; + _fltOpt->getWriteResult().warn( warning ); + return; + } + + _records->writeInt16( (int16) OBJECT_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt32( ord->_flags ); + _records->writeInt16( ord->_relativePriority ); + _records->writeUInt16( ord->_transparency ); + _records->writeUInt16( ord->_effectID1 ); + _records->writeUInt16( ord->_effectID2 ); + _records->writeUInt16( ord->_significance ); + _records->writeUInt16( 0 ); // reserved +} + +void +FltExportVisitor::writeDegreeOfFreedom( const osgSim::DOFTransform* dof ) +{ + const osg::Matrix& invPut = dof->getInversePutMatrix(); + + // Origin of DOF coord sys + osg::Vec3d origin( invPut.getTrans() ); + + osg::Vec3 xAxis( invPut(0,0), invPut(0,1), invPut(0,2) ); + osg::Vec3 yAxis( invPut(1,0), invPut(1,1), invPut(1,2) ); + // Reference point along DOF coord sys's X axis + osg::Vec3d pointOnXAxis = origin + xAxis; + // Reference point in DOF coord sys's X-Y plane + osg::Vec3d pointInXYPlane = origin + yAxis; + + // Translations + osg::Vec3d minTranslate( dof->getMinTranslate() ); + osg::Vec3d maxTranslate( dof->getMaxTranslate() ); + osg::Vec3d currTranslate( dof->getCurrentTranslate() ); + osg::Vec3d incrTranslate( dof->getIncrementTranslate() ); + + // Rotations + osg::Vec3d minHPR( dof->getMinHPR() ); + osg::Vec3d maxHPR( dof->getMaxHPR() ); + osg::Vec3d currHPR( dof->getCurrentHPR() ); + osg::Vec3d incrHPR( dof->getIncrementHPR() ); + + // Scaling + osg::Vec3d minScale( dof->getMinScale() ); + osg::Vec3d maxScale( dof->getMaxScale() ); + osg::Vec3d currScale( dof->getCurrentScale() ); + osg::Vec3d incrScale( dof->getIncrementScale() ); + + + uint16 length( 384 ); + IdHelper id(*this, dof->getName() ); + + _records->writeInt16( (int16) DOF_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // 'Reserved' (unused) + _records->writeVec3d( origin ); + _records->writeVec3d( pointOnXAxis ); + _records->writeVec3d( pointInXYPlane ); + + // Translations + _records->writeFloat64( minTranslate.z() ); + _records->writeFloat64( maxTranslate.z() ); + _records->writeFloat64( currTranslate.z() ); + _records->writeFloat64( incrTranslate.z() ); + + _records->writeFloat64( minTranslate.y() ); + _records->writeFloat64( maxTranslate.y() ); + _records->writeFloat64( currTranslate.y() ); + _records->writeFloat64( incrTranslate.y() ); + + _records->writeFloat64( minTranslate.x() ); + _records->writeFloat64( maxTranslate.x() ); + _records->writeFloat64( currTranslate.x() ); + _records->writeFloat64( incrTranslate.x() ); + + // Rotations: 0 = Yaw, 1 = Pitch, 2 = Roll + _records->writeFloat64( osg::RadiansToDegrees(minHPR[1]) ); + _records->writeFloat64( osg::RadiansToDegrees(maxHPR[1]) ); + _records->writeFloat64( osg::RadiansToDegrees(currHPR[1]) ); + _records->writeFloat64( osg::RadiansToDegrees(incrHPR[1]) ); + + _records->writeFloat64( osg::RadiansToDegrees(minHPR[2]) ); + _records->writeFloat64( osg::RadiansToDegrees(maxHPR[2]) ); + _records->writeFloat64( osg::RadiansToDegrees(currHPR[2]) ); + _records->writeFloat64( osg::RadiansToDegrees(incrHPR[2]) ); + + _records->writeFloat64( osg::RadiansToDegrees(minHPR[0]) ); + _records->writeFloat64( osg::RadiansToDegrees(maxHPR[0]) ); + _records->writeFloat64( osg::RadiansToDegrees(currHPR[0]) ); + _records->writeFloat64( osg::RadiansToDegrees(incrHPR[0]) ); + + // Scaling + _records->writeFloat64( minScale.z() ); + _records->writeFloat64( maxScale.z() ); + _records->writeFloat64( currScale.z() ); + _records->writeFloat64( incrScale.z() ); + + _records->writeFloat64( minScale.y() ); + _records->writeFloat64( maxScale.y() ); + _records->writeFloat64( currScale.y() ); + _records->writeFloat64( incrScale.y() ); + + _records->writeFloat64( minScale.x() ); + _records->writeFloat64( maxScale.x() ); + _records->writeFloat64( currScale.x() ); + _records->writeFloat64( incrScale.y() ); + + _records->writeInt32( dof->getLimitationFlags() ); // Constraint flags + _records->writeInt32( 0 ); // 'Reserved' (unused) + +} + +// Parent pool override flags +static const unsigned long COLOR_PALETTE_OVERRIDE = 0x80000000u >> 0; +static const unsigned long MATERIAL_PALETTE_OVERRIDE = 0x80000000u >> 1; +static const unsigned long TEXTURE_PALETTE_OVERRIDE = 0x80000000u >> 2; +static const unsigned long LINE_STYLE_PALETTE_OVERRIDE = 0x80000000u >> 3; +static const unsigned long SOUND_PALETTE_OVERRIDE = 0x80000000u >> 4; +static const unsigned long LIGHT_SOURCE_PALETTE_OVERRIDE = 0x80000000u >> 5; +static const unsigned long LIGHT_POINT_PALETTE_OVERRIDE = 0x80000000u >> 6; +static const unsigned long SHADER_PALETTE_OVERRIDE = 0x80000000u >> 7; + +void +FltExportVisitor::writeExternalReference( const osg::ProxyNode& proxy ) +{ + uint16 length( 216 ); + + // Set sane defaults for the override flags + unsigned long flags = COLOR_PALETTE_OVERRIDE | + MATERIAL_PALETTE_OVERRIDE | + TEXTURE_PALETTE_OVERRIDE | + LIGHT_POINT_PALETTE_OVERRIDE | + SHADER_PALETTE_OVERRIDE ; + + // Selectively turn off overrides for resources we don't need + const ParentPools* pp = static_cast(proxy.getUserData() ); + if (pp && pp->getColorPool() ) + flags &= ~COLOR_PALETTE_OVERRIDE; + + if (pp && pp->getMaterialPool() ) + flags &= ~MATERIAL_PALETTE_OVERRIDE; + + if (pp && pp->getTexturePool() ) + flags &= ~TEXTURE_PALETTE_OVERRIDE; + + if (pp && pp->getLightSourcePool() ) + flags &= ~LIGHT_SOURCE_PALETTE_OVERRIDE; + + if (pp && pp->getLPAppearancePool() ) + flags &= ~LIGHT_POINT_PALETTE_OVERRIDE; + + if (pp && pp->getShaderPool() ) + flags &= ~SHADER_PALETTE_OVERRIDE; + + _records->writeInt16( (int16) EXTERNAL_REFERENCE_OP ); + _records->writeInt16( length ); + _records->writeString(proxy.getFileName(0), 200); + _records->writeInt32(0); // Reserved + _records->writeInt32(flags); + _records->writeInt16(0); // ViewAsBoundingBox flag + _records->writeInt16(0); // Reserved +} + +void +FltExportVisitor::writeLevelOfDetail( const osg::LOD& lod, + osg::Vec3d const& center, + double switchInDist, + double switchOutDist) +{ + uint16 length( 80 ); + IdHelper id(*this, lod.getName() ); + + _records->writeInt16( (int16) LOD_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // 'Reserved' field + _records->writeFloat64( switchInDist ); + _records->writeFloat64( switchOutDist ); // Switch-out distance + _records->writeInt16( 0 ); // Special Effect ID1 + _records->writeInt16( 0 ); // Special Effect ID2 + _records->writeInt32( 0 ); // Flags + _records->writeFloat64( center.x() ); + _records->writeFloat64( center.y() ); + _records->writeFloat64( center.z() ); + _records->writeFloat64( 0 ); // Transition range + _records->writeFloat64( 0 ); // Significant size + +} + +void +FltExportVisitor::writeLightSource( const osg::LightSource& node ) +{ + + static const unsigned int ENABLED = 0x80000000u >> 0; + static const unsigned int GLOBAL = 0x80000000u >> 1; + + osg::Light const* light = node.getLight(); + int index = _lightSourcePalette->add(light); + + osg::Vec4d const& lightPos = light->getPosition(); + osg::Vec3f const& lightDir = light->getDirection(); + + uint32 flags = 0; + osg::StateSet const* ss = getCurrentStateSet(); + if (ss->getMode(GL_LIGHT0 + light->getLightNum() ) & osg::StateAttribute::ON) + { + flags |= ENABLED; + } + + // If this light is enabled for the node at the top of our StateSet stack, + // assume it is 'global' for OpenFlight's purposes + ss = _stateSetStack.front().get(); + if (ss->getMode(GL_LIGHT0 + light->getLightNum() ) & osg::StateAttribute::ON) + { + flags |= GLOBAL; + } + + uint16 length( 64 ); + IdHelper id(*this, node.getName() ); + + _records->writeInt16( (int16) LIGHT_SOURCE_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // Reserved + _records->writeInt32( index ); // Index into light source palette + _records->writeInt32( 0 ); // Reserved + _records->writeUInt32( flags ); // Flags + _records->writeInt32( 0 ); // Reserved + _records->writeVec3d( osg::Vec3d( + lightPos.x() , lightPos.y() , lightPos.z() ) ); + + // TODO: Verify that indices 0 and 1 correspond to yaw and pitch + _records->writeFloat32( lightDir[0] ); // Yaw + _records->writeFloat32( lightDir[1] ); // Pitch +} + + + +void +FltExportVisitor::writeSwitch( const osgSim::MultiSwitch* ms ) +{ + int32 currMask = ms->getActiveSwitchSet(); + int32 numMasks = ms->getSwitchSetList().size(); + int32 numWordsPerMask = ms->getNumChildren() / 32; + if (ms->getNumChildren() % 32 != 0) ++numWordsPerMask; + + uint16 length( 28 + numMasks * numWordsPerMask * sizeof(int32) ); + IdHelper id(*this, ms->getName() ); + + _records->writeInt16( (int16) SWITCH_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // <-- 'Reserved' (unused) + _records->writeInt32( currMask ); + _records->writeInt32( numMasks ); + _records->writeInt32( numWordsPerMask ); + + // For each mask... + for (int i = 0; i < numMasks; ++i) + { + // ... write out the set of 32-bit words comprising the mask + uint32 maskWord = 0; + const osgSim::MultiSwitch::ValueList& maskBits = ms->getValueList(i); + + for (size_t j = 0; j < maskBits.size(); ++j) + { + // If this bit is set, set the corresponding mask word + if (maskBits[j]) maskWord |= 1 << (j % 32); + + // If we just set the 31st (last) bit of the current word, need + // to write it out and reset prior to continuing the loop + if ( (j + 1) % 32 == 0 ) + { + _records->writeUInt32(maskWord); + maskWord = 0; + } + } + + // If the mask size wasn't a multiple of 32, need to write out + // the final word containing the 'remainder' bits + if (maskBits.size() % 32 != 0) + { + _records->writeUInt32(maskWord); + } + } + +} + + +void +FltExportVisitor::writeSwitch( const osg::Switch* sw ) +{ + // An osg::Switch is just a special case of an osgSim::MultiSwitch + // that only has a single mask + int32 currMask = 0; + int32 numMasks = 1; + int32 numWordsPerMask = sw->getNumChildren() / 32; + if (sw->getNumChildren() % 32 != 0) ++numWordsPerMask; + + uint16 length( 28 + numMasks * numWordsPerMask * sizeof(int32) ); + IdHelper id(*this, sw->getName() ); + + _records->writeInt16( (int16) SWITCH_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt32( 0 ); // <-- 'Reserved' (unused) + _records->writeInt32( currMask ); + _records->writeInt32( numMasks ); + _records->writeInt32( numWordsPerMask ); + + // Bust the mask up into as many 32-bit words as are necessary to hold it + uint32 maskWord = 0; + const osg::Switch::ValueList& maskBits = sw->getValueList(); + + for (size_t i = 0; i < maskBits.size(); ++i) + { + // If this bit is set, set the corresponding mask word + if (maskBits[i]) maskWord |= 1 << (i % 32); + + // If we just set the 31st (last) bit of the current word, need + // to write it out and reset prior to continuing the loop + if ( (i + 1) % 32 == 0 ) + { + _records->writeUInt32(maskWord); + maskWord = 0; + } + } + + // If the mask size wasn't a multiple of 32, need to write out + // the final word containing the 'remainder' bits + if (maskBits.size() % 32 != 0) + { + _records->writeUInt32(maskWord); + } + +} + +void +FltExportVisitor::writeLightPoint( const osgSim::LightPointNode* lpn ) +{ + enum Directionality + { + OMNIDIRECTIONAL = 0, + UNIDIRECTIONAL = 1, + BIDIRECTIONAL = 2 + }; + enum DisplayMode + { + RASTER = 0, + CALLIG = 1, + EITHER = 2 + }; + enum Modes + { + ENABLE = 0, + DISABLE = 1 + }; + enum Flags + { + NO_BACK_COLOR = 0x80000000u >> 1, + CALLIGRAPHIC = 0x80000000u >> 3, + REFLECTIVE = 0x80000000u >> 4, + PERSPECTIVE = 0x80000000u >> 8, + FLASHING = 0x80000000u >> 9, + ROTATING = 0x80000000u >> 10, + ROTATE_CC = 0x80000000u >> 11, + VISIBLE_DAY = 0x80000000u >> 15, + VISIBLE_DUSK = 0x80000000u >> 16, + VISIBLE_NIGHT = 0x80000000u >> 17 + }; + int32 flags( NO_BACK_COLOR ); + + if (lpn->getNumLightPoints() == 0) + return; + + // In OSG, each LightPoint within a LightPointNode can have different appearance + // parameters, but in OpenFlight, a Light Point Record contains a list of homogenous + // vertices. To be correct, we'd have to look at all LightPoints in the LightPointNode + // and spew out multiple FLT records for each group that shared common appearance + // parameters. Instead, we cheat: We take the first LightPoint and use its appearance + // parameters for all LightPoints in the LightPointNode. + const osgSim::LightPoint& lp0 = lpn->getLightPoint( 0 ); + + // No really good mapping between OSG and FLT light point animations. + float32 animPeriod( 0.f ); + float32 animEnabled( 0.f ); + float32 animPhaseDelay( 0.f ); + if (lp0._blinkSequence != NULL) + { + flags |= FLASHING; + animPeriod = 4.f; + animEnabled = 2.f; + animPhaseDelay = lp0._blinkSequence->getPhaseShift(); + } + + // Note that true bidirectional light points are currently unsupported (they are inavailable + // in OSG, so we never write them out to FLT as BIDIRECTIONAL. + int32 directionality( OMNIDIRECTIONAL ); + float32 horizLobe( 360.f ); + float32 vertLobe( 360.f ); + float32 lobeRoll( 0.f ); + const osgSim::DirectionalSector* ds = dynamic_cast< osgSim::DirectionalSector* >( lp0._sector.get() ); + if (ds) + { + directionality = UNIDIRECTIONAL; + horizLobe = osg::RadiansToDegrees( ds->getHorizLobeAngle() ); + vertLobe = osg::RadiansToDegrees( ds->getVertLobeAngle() ); + lobeRoll = osg::RadiansToDegrees( ds->getLobeRollAngle() ); + } + + { + // Braces req'd to invoke idHelper destructor (and potentially + // write LongID record) before Push Record. + + const uint16 length( 156 ); + IdHelper id( *this, lpn->getName() ); + + _records->writeInt16( (int16) LIGHT_POINT_OP ); + _records->writeInt16( length ); + _records->writeID( id ); + _records->writeInt16( 0 ); // Surface material code + _records->writeInt16( 0 ); // Feature ID + _records->writeUInt32( -1 ); // Back color for bidirectional + _records->writeInt32( EITHER ); // Display mode + _records->writeFloat32( lp0._intensity ); // Intensity + _records->writeFloat32( 0.f ); // Back intensity TBD + _records->writeFloat32( 0.f ); // min defocus + _records->writeFloat32( 0.f ); // max defocus + _records->writeInt32( DISABLE ); // Fading mode + _records->writeInt32( DISABLE ); // Fog punch mode + _records->writeInt32( DISABLE ); // Directional mode + _records->writeInt32( 0 ); // Range mode + _records->writeFloat32( lpn->getMinPixelSize() ); // min pixel size + _records->writeFloat32( lpn->getMaxPixelSize() ); // max pixel size + _records->writeFloat32( lp0._radius * 2.f ); // Actual size + _records->writeFloat32( 1.f ); // transparent falloff pixel size + _records->writeFloat32( 1.f ); // Transparent falloff exponent + _records->writeFloat32( 1.f ); // Transparent falloff scalar + _records->writeFloat32( 0.f ); // Transparent falloff clamp + _records->writeFloat32( 1.f ); // Fog scalar + _records->writeFloat32( 0.f ); // Reserved + _records->writeFloat32( 0.f ); // Size difference threshold + _records->writeInt32( directionality ); // Directionality + _records->writeFloat32( horizLobe ); // Horizontal lobe angle + _records->writeFloat32( vertLobe ); // Vertical lobe angle + _records->writeFloat32( lobeRoll ); // Lobe roll angle + _records->writeFloat32( 0.f ); // Directional falloff exponent + _records->writeFloat32( 0.f ); // Directional ambient intensity + _records->writeFloat32( animPeriod ); // Animation period in seconds + _records->writeFloat32( animPhaseDelay ); // Animation phase delay in secnds + _records->writeFloat32( animEnabled ); // Animation enabled period in seconds + _records->writeFloat32( 1.f ); // Significance + _records->writeInt32( 0 ); // Calligraphic draw order + _records->writeInt32( flags ); // Flags + _records->writeVec3f( osg::Vec3f( 0.f, 0.f, 0.f ) ); // Axis of rotation + } + + { + osg::ref_ptr< osg::Vec3Array > v = new osg::Vec3Array( lpn->getNumLightPoints() ); + osg::ref_ptr< osg::Vec4Array > c = new osg::Vec4Array( lpn->getNumLightPoints() ); + osg::ref_ptr< osg::Vec3Array > n = new osg::Vec3Array( lpn->getNumLightPoints() ); + osg::Vec3f normal( 0.f, 0.f, 1.f ); + + unsigned int idx; + for( idx=0; idxgetNumLightPoints(); idx++) + { + const osgSim::LightPoint& lp = lpn->getLightPoint( idx ); + (*v)[ idx ] = lp._position; + (*c)[ idx ] = lp._color; + + const osgSim::DirectionalSector* ds = dynamic_cast< osgSim::DirectionalSector* >( lp._sector.get() ); + if (ds) + normal = ds->getDirection(); + (*n)[ idx ] = normal; + } + _vertexPalette->add( v.get(), c.get(), n.get(), NULL, true, true, false ); + } + + writeMatrix( lpn->getUserData() ); + writeComment( *lpn ); + writePush(); + writeVertexList( 0, lpn->getNumLightPoints() ); + writePop(); +} + + +void +FltExportVisitor::writeColorPalette() +{ + // FLT exporter doesn't use a color palette but writes + // a bogus one to satisfy loaders that require it. + uint16 length( 4228 ); + + _dos.writeInt16( (int16) COLOR_PALETTE_OP ); + _dos.writeInt16( length ); + _dos.writeFill( 128 ); // Reserved + int idx; + for( idx=0; idx<1024; idx++) + _dos.writeUInt32( 0xffffffff ); // Color n +} + + + + +}