From Wang Rui, "I've initially added the XML support of the new native osg format,

using osgDB::XmlParser. The extension for XML-formatted scenes is
.osgx, corresponding to .osgb for binary and .osgt for ascii. It could
either be rendered in osgviewer or edited by common web browsers and
xml editors because of a range of changes to fit the XML syntax. For
example, the recorded class names are slight modified, from
'osg::Geode' to 'osg--Geode'.

To quickly get an XML file:
# ./osgconv cow.osg cow.osgx

The StreamOperator header, InputStreram and OutputStream classes are
modified to be more portable for triple ascii/binary/XML formats. I
also fixed a bug in readImage()/writeImage() to share image objects if
needed.

The ReaderWriterOSG2 class now supports all three formats and
reading/writing scene objects (not nodes or images), thanks to
Torben's advice before.
"
This commit is contained in:
Robert Osfield 2010-03-10 13:48:41 +00:00
parent ce19b37981
commit e082b01f26
9 changed files with 778 additions and 168 deletions

View File

@ -61,7 +61,8 @@ public:
{ {
READ_UNKNOWN = 0, READ_UNKNOWN = 0,
READ_SCENE, READ_SCENE,
READ_IMAGE READ_IMAGE,
READ_OBJECT
}; };
InputStream( const osgDB::Options* options ); InputStream( const osgDB::Options* options );
@ -123,9 +124,9 @@ public:
{ ptr = static_cast<T*>(readObject()); return *this; } { ptr = static_cast<T*>(readObject()); return *this; }
// Convenient methods for reading // Convenient methods for reading
bool matchString( const std::string& str ); bool matchString( const std::string& str ) { return _in->matchString(str); }
void advanceToCurrentEndBracket(); void advanceToCurrentEndBracket() { _in->advanceToCurrentEndBracket(); }
void readWrappedString( std::string& str ); void readWrappedString( std::string& str ) { _in->readWrappedString(str); checkStream(); }
void readCharArray( char* s, unsigned int size ) { _in->readCharArray(s, size); } void readCharArray( char* s, unsigned int size ) { _in->readCharArray(s, size); }
// readSize() use unsigned int for all sizes. // readSize() use unsigned int for all sizes.

View File

@ -60,7 +60,8 @@ public:
{ {
WRITE_UNKNOWN = 0, WRITE_UNKNOWN = 0,
WRITE_SCENE, WRITE_SCENE,
WRITE_IMAGE WRITE_IMAGE,
WRITE_OBJECT
}; };
enum WriteImageHint enum WriteImageHint
@ -133,7 +134,7 @@ public:
{ writeObject(ptr.get()); return *this; } { writeObject(ptr.get()); return *this; }
// Convenient methods for writing // Convenient methods for writing
void writeWrappedString( const std::string& str ); void writeWrappedString( const std::string& str ) { _out->writeWrappedString(str); }
void writeCharArray( const char* s, unsigned int size ) { _out->writeCharArray(s, size); } void writeCharArray( const char* s, unsigned int size ) { _out->writeCharArray(s, size); }
// method for converting all data structure sizes to unsigned int to ensure architecture portability. // method for converting all data structure sizes to unsigned int to ensure architecture portability.

View File

@ -39,6 +39,9 @@ public:
virtual void writeProperty( const ObjectProperty& prop ) = 0; virtual void writeProperty( const ObjectProperty& prop ) = 0;
virtual void writeMark( const ObjectMark& mark ) = 0; virtual void writeMark( const ObjectMark& mark ) = 0;
virtual void writeCharArray( const char* s, unsigned int size ) = 0; virtual void writeCharArray( const char* s, unsigned int size ) = 0;
virtual void writeWrappedString( const std::string& str ) = 0;
virtual void flush() { _out->flush(); }
protected: protected:
std::ostream* _out; std::ostream* _out;
@ -79,6 +82,10 @@ public:
virtual void readProperty( ObjectProperty& prop ) = 0; virtual void readProperty( ObjectProperty& prop ) = 0;
virtual void readMark( ObjectMark& mark ) = 0; virtual void readMark( ObjectMark& mark ) = 0;
virtual void readCharArray( char* s, unsigned int size ) = 0; virtual void readCharArray( char* s, unsigned int size ) = 0;
virtual void readWrappedString( std::string& str ) = 0;
virtual bool matchString( const std::string& str ) { return false; }
virtual void advanceToCurrentEndBracket() {}
protected: protected:
std::istream* _in; std::istream* _in;

View File

@ -161,77 +161,6 @@ InputStream& InputStream::operator>>( osg::Matrixd& mat )
return *this; return *this;
} }
bool InputStream::matchString( const std::string& str )
{
if ( !isBinary() )
{
std::string s; *this >> s;
if ( s==str ) return true;
else _in->getStream()->seekg( -(int)(s.length()), std::ios::cur );
}
return false;
}
void InputStream::advanceToCurrentEndBracket()
{
if ( isBinary() )
return;
std::string passString;
unsigned int blocks = 0;
while ( !_in->getStream()->eof() )
{
passString.clear();
*this >> passString;
if ( passString=="}" )
{
if ( blocks<=0 ) return;
else blocks--;
}
else if ( passString=="{" )
blocks++;
}
}
void InputStream::readWrappedString( std::string& str )
{
*this >> str;
if ( !isBinary() )
{
if ( str[0]=='\"' )
{
if ( str.size()==1 || (*str.rbegin())!='\"' )
{
char ch;
do
{
_in->getStream()->get( ch ); checkStream();
if ( ch=='\\' )
{
_in->getStream()->get( ch ); checkStream();
if ( ch=='\"' )
{
str += ch; ch = 0;
}
else if ( ch=='\\' )
{
str += ch;
}
else
{
str += '\\'; str += ch;
}
}
else
str += ch;
} while ( ch!='\"' );
}
str = str.substr(1, str.size()-2);
}
}
}
osg::Array* InputStream::readArray() osg::Array* InputStream::readArray()
{ {
osg::ref_ptr<osg::Array> array = NULL; osg::ref_ptr<osg::Array> array = NULL;
@ -486,6 +415,17 @@ osg::PrimitiveSet* InputStream::readPrimitiveSet()
osg::Image* InputStream::readImage() osg::Image* InputStream::readImage()
{ {
unsigned int id = 0;
*this >> PROPERTY("ImageID") >> id;
if ( getException() ) return NULL;
IdentifierMap::iterator itr = _identifierMap.find( id );
if ( itr!=_identifierMap.end() )
{
advanceToCurrentEndBracket();
return static_cast<osg::Image*>( itr->second.get() );
}
std::string name; std::string name;
int writeHint, decision = IMAGE_EXTERNAL; int writeHint, decision = IMAGE_EXTERNAL;
*this >> PROPERTY("FileName"); readWrappedString(name); *this >> PROPERTY("FileName"); readWrappedString(name);
@ -693,6 +633,7 @@ InputStream::ReadType InputStream::start( InputIterator* inIterator )
std::string typeString; *this >> typeString; std::string typeString; *this >> typeString;
if ( typeString=="Scene" ) type = READ_SCENE; if ( typeString=="Scene" ) type = READ_SCENE;
else if ( typeString=="Image" ) type = READ_IMAGE; else if ( typeString=="Image" ) type = READ_IMAGE;
else if ( typeString=="Object" ) type = READ_OBJECT;
std::string osgName, osgVersion; std::string osgName, osgVersion;
*this >> PROPERTY("#Version") >> version; *this >> PROPERTY("#Version") >> version;
@ -712,12 +653,16 @@ InputStream::ReadType InputStream::start( InputIterator* inIterator )
void InputStream::decompress() void InputStream::decompress()
{ {
if ( !isBinary() ) return;
_fields.clear(); _fields.clear();
_fields.push_back( "Decompression" ); _fields.push_back( "Decompression" );
if ( !isBinary() ) return;
std::string compressorName; *this >> compressorName; std::string compressorName; *this >> compressorName;
if ( compressorName=="0" ) return; if ( compressorName=="0" )
{
_fields.pop_back();
return;
}
BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(compressorName); BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(compressorName);
if ( !compressor ) if ( !compressor )
@ -773,7 +718,7 @@ void InputStream::readArrayImplementation( T* a, int read_size, bool useByteSwap
a->resize( size ); a->resize( size );
if ( isBinary() ) if ( isBinary() )
{ {
_in->getStream()->read( (char*)&((*a)[0]), read_size*size ); checkStream(); readCharArray( (char*)&((*a)[0]), read_size*size ); checkStream();
if ( useByteSwap && _byteSwap ) if ( useByteSwap && _byteSwap )
{ {
for ( int i=0; i<size; ++i ) for ( int i=0; i<size; ++i )

View File

@ -132,28 +132,6 @@ OutputStream& OutputStream::operator<<( const osg::Matrixd& mat )
return *this; return *this;
} }
void OutputStream::writeWrappedString( const std::string& str )
{
if ( !isBinary() )
{
std::string wrappedStr;
unsigned int size = str.size();
for ( unsigned int i=0; i<size; ++i )
{
char ch = str[i];
if ( ch=='\"' ) wrappedStr += '\\';
else if ( ch=='\\' ) wrappedStr += '\\';
wrappedStr += ch;
}
wrappedStr.insert( 0, 1, '\"' );
wrappedStr += '\"';
*this << wrappedStr;
}
else
*this << str;
}
void OutputStream::writeArray( const osg::Array* a ) void OutputStream::writeArray( const osg::Array* a )
{ {
if ( !a ) return; if ( !a ) return;
@ -312,6 +290,10 @@ void OutputStream::writeImage( const osg::Image* img )
{ {
if ( !img ) return; if ( !img ) return;
unsigned int id = findOrCreateObjectID( img );
*this << PROPERTY("ImageID") << id << std::endl; // Write image ID
if ( getException() ) return;
*this << PROPERTY("FileName"); writeWrappedString(img->getFileName()); *this << std::endl; *this << PROPERTY("FileName"); writeWrappedString(img->getFileName()); *this << std::endl;
*this << PROPERTY("WriteHint") << (int)img->getWriteHint(); *this << PROPERTY("WriteHint") << (int)img->getWriteHint();
if ( getException() ) return; if ( getException() ) return;
@ -487,7 +469,7 @@ void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType t
else else
{ {
*this << _compressorName; *this << _compressorName;
_out->getStream()->flush(); _out->flush();
_out->setStream( &_compressSource ); _out->setStream( &_compressSource );
return; return;
} }
@ -501,6 +483,7 @@ void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType t
{ {
case WRITE_SCENE: typeString = "Scene"; break; case WRITE_SCENE: typeString = "Scene"; break;
case WRITE_IMAGE: typeString = "Image"; break; case WRITE_IMAGE: typeString = "Image"; break;
case WRITE_OBJECT: typeString = "Object"; break;
default: break; default: break;
} }
@ -515,12 +498,16 @@ void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType t
void OutputStream::compress( std::ostream* ostream ) void OutputStream::compress( std::ostream* ostream )
{ {
if ( _compressorName.empty() || !isBinary() ) return;
_fields.clear(); _fields.clear();
_fields.push_back( "Compression" ); _fields.push_back( "Compression" );
if ( _compressorName.empty() || !isBinary() ) return;
BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(_compressorName); BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(_compressorName);
if ( !compressor || !ostream ) return; if ( !compressor || !ostream )
{
_fields.pop_back();
return;
}
if ( !compressor->compress(*ostream, _compressSource.str()) ) if ( !compressor->compress(*ostream, _compressSource.str()) )
throwException( "OutputStream: Failed to compress stream." ); throwException( "OutputStream: Failed to compress stream." );

View File

@ -100,6 +100,23 @@ public:
virtual void writeCharArray( const char* s, unsigned int size ) {} virtual void writeCharArray( const char* s, unsigned int size ) {}
virtual void writeWrappedString( const std::string& str )
{
std::string wrappedStr;
unsigned int size = str.size();
for ( unsigned int i=0; i<size; ++i )
{
char ch = str[i];
if ( ch=='\"' ) wrappedStr += '\\';
else if ( ch=='\\' ) wrappedStr += '\\';
wrappedStr += ch;
}
wrappedStr.insert( 0, 1, '\"' );
wrappedStr += '\"';
writeString( wrappedStr );
}
protected: protected:
bool _readyForEndBracket; bool _readyForEndBracket;
int _indent; int _indent;
@ -212,6 +229,68 @@ public:
} }
virtual void readCharArray( char* s, unsigned int size ) {} virtual void readCharArray( char* s, unsigned int size ) {}
virtual void readWrappedString( std::string& str )
{
readString( str );
if ( str[0]=='\"' )
{
if ( str.size()==1 || (*str.rbegin())!='\"' )
{
char ch;
do
{
_in->get( ch ); checkStream();
if ( ch=='\\' )
{
_in->get( ch ); checkStream();
if ( ch=='\"' )
{
str += ch; ch = 0;
}
else if ( ch=='\\' )
{
str += ch;
}
else
{
str += '\\'; str += ch;
}
}
else
str += ch;
} while ( ch!='\"' );
}
str = str.substr(1, str.size()-2);
}
}
virtual bool matchString( const std::string& str )
{
std::string s; readString(s);
if ( s==str ) return true;
else _in->seekg( -(int)(s.length()), std::ios::cur );
return false;
}
virtual void advanceToCurrentEndBracket()
{
std::string passString;
unsigned int blocks = 0;
while ( !_in->eof() )
{
passString.clear();
readString( passString );
if ( passString=="}" )
{
if ( blocks<=0 ) return;
else blocks--;
}
else if ( passString=="{" )
blocks++;
}
}
}; };
#endif #endif

View File

@ -65,6 +65,9 @@ public:
virtual void writeCharArray( const char* s, unsigned int size ) virtual void writeCharArray( const char* s, unsigned int size )
{ if ( size>0 ) _out->write( s, size ); } { if ( size>0 ) _out->write( s, size ); }
virtual void writeWrappedString( const std::string& str )
{ writeString( str ); }
}; };
class BinaryInputIterator : public osgDB::InputIterator class BinaryInputIterator : public osgDB::InputIterator
@ -177,6 +180,9 @@ public:
virtual void readCharArray( char* s, unsigned int size ) virtual void readCharArray( char* s, unsigned int size )
{ if ( size>0 ) _in->read( s, size ); } { if ( size>0 ) _in->read( s, size ); }
virtual void readWrappedString( std::string& str )
{ readString( str ); }
protected: protected:
int _byteSwap; int _byteSwap;
}; };

View File

@ -18,6 +18,7 @@
#include <osgDB/ObjectWrapper> #include <osgDB/ObjectWrapper>
#include "AsciiStreamOperator.h" #include "AsciiStreamOperator.h"
#include "BinaryStreamOperator.h" #include "BinaryStreamOperator.h"
#include "XmlStreamOperator.h"
using namespace osgDB; using namespace osgDB;
@ -26,11 +27,15 @@ using namespace osgDB;
InputIterator* readInputIterator( std::istream& fin, const Options* options ) InputIterator* readInputIterator( std::istream& fin, const Options* options )
{ {
bool extensionIsAscii = false; bool extensionIsAscii = false, extensionIsXML = false;
if ( options && options->getOptionString().find("Ascii")!=std::string::npos ) if ( options )
extensionIsAscii = true; {
const std::string& optionString = options->getOptionString();
if ( optionString.find("Ascii")!=std::string::npos ) extensionIsAscii = true;
else if ( optionString.find("XML")!=std::string::npos ) extensionIsXML = true;
}
if ( !extensionIsAscii ) if ( !extensionIsAscii && !extensionIsXML )
{ {
unsigned int headerLow = 0, headerHigh = 0; unsigned int headerLow = 0, headerHigh = 0;
fin.read( (char*)&headerLow, INT_SIZE ); fin.read( (char*)&headerLow, INT_SIZE );
@ -42,10 +47,24 @@ InputIterator* readInputIterator( std::istream& fin, const Options* options )
fin.seekg( 0, std::ios::beg ); fin.seekg( 0, std::ios::beg );
} }
std::string header; fin >> header; if ( !extensionIsXML )
if ( header=="#Ascii" )
{ {
return new AsciiInputIterator(&fin); std::string header; fin >> header;
if ( header=="#Ascii" )
{
return new AsciiInputIterator(&fin);
}
fin.seekg( 0, std::ios::beg );
}
if ( 1 )
{
std::string header; std::getline( fin, header );
if ( !header.compare(0, 5, "<?xml") )
{
return new XmlInputIterator(&fin);
}
fin.seekg( 0, std::ios::beg );
} }
return NULL; return NULL;
} }
@ -57,6 +76,11 @@ OutputIterator* writeInputIterator( std::ostream& fout, const Options* options )
fout << std::string("#Ascii") << ' '; fout << std::string("#Ascii") << ' ';
return new AsciiOutputIterator(&fout); return new AsciiOutputIterator(&fout);
} }
else if ( options && options->getOptionString().find("XML")!=std::string::npos )
{
fout << std::string("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>") << std::endl;
return new XmlOutputIterator(&fout);
}
else else
{ {
unsigned int low = OSG_HEADER_LOW, high = OSG_HEADER_HIGH; unsigned int low = OSG_HEADER_LOW, high = OSG_HEADER_HIGH;
@ -74,8 +98,10 @@ public:
supportsExtension( "osg2", "OpenSceneGraph extendable format" ); supportsExtension( "osg2", "OpenSceneGraph extendable format" );
supportsExtension( "osgt", "OpenSceneGraph extendable ascii format" ); supportsExtension( "osgt", "OpenSceneGraph extendable ascii format" );
supportsExtension( "osgb", "OpenSceneGraph extendable binary format" ); supportsExtension( "osgb", "OpenSceneGraph extendable binary format" );
supportsExtension( "osgx", "OpenSceneGraph extendable XML format" );
supportsOption( "Ascii", "Import/Export option: Force reading/writing ascii file" ); supportsOption( "Ascii", "Import/Export option: Force reading/writing ascii file" );
supportsOption( "XML", "Import/Export option: Force reading/writing XML file" );
supportsOption( "ForceReadingImage", "Import option: Load an empty image instead if required file missed" ); supportsOption( "ForceReadingImage", "Import option: Load an empty image instead if required file missed" );
supportsOption( "SchemaFile=<file>", "Import/Export option: Use/Record a ascii schema file" ); supportsOption( "SchemaFile=<file>", "Import/Export option: Use/Record a ascii schema file" );
supportsOption( "Compressor=<name>", "Export option: Use an inbuilt or user-defined compressor" ); supportsOption( "Compressor=<name>", "Export option: Use an inbuilt or user-defined compressor" );
@ -99,26 +125,58 @@ public:
virtual const char* className() const virtual const char* className() const
{ return "OpenSceneGraph Native Format Reader/Writer"; } { return "OpenSceneGraph Native Format Reader/Writer"; }
virtual ReadResult readObject( const std::string& file, const Options* options ) const Options* prepareReading( ReadResult& result, std::string& fileName, const Options* options ) const
{ return readNode(file, options); }
virtual ReadResult readObject( std::istream& fin, const Options* options ) const
{ return readNode(fin, options); }
virtual ReadResult readImage( const std::string& file, const Options* options ) const
{ {
std::string ext = osgDB::getLowerCaseFileExtension( file ); std::string ext = osgDB::getLowerCaseFileExtension( fileName );
if ( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED; if ( !acceptsExtension(ext) ) result = ReadResult::FILE_NOT_HANDLED;
std::string fileName = osgDB::findDataFile( file, options ); fileName = osgDB::findDataFile( fileName, options );
if ( fileName.empty() ) return ReadResult::FILE_NOT_FOUND; if ( fileName.empty() ) result = ReadResult::FILE_NOT_FOUND;
osg::ref_ptr<Options> local_opt = options ? osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName)); local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" ); if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
return local_opt.release();
}
virtual ReadResult readObject( const std::string& file, const Options* options ) const
{
ReadResult result = ReadResult::FILE_LOADED;
std::string fileName = file;
Options* local_opt = prepareReading( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary ); osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary );
return readImage( istream, local_opt.get() ); return readObject( istream, local_opt );
}
virtual ReadResult readObject( std::istream& fin, const Options* options ) const
{
osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
if ( !ii ) return ReadResult::FILE_NOT_HANDLED;
InputStream is( options );
if ( is.start(ii.get())!=InputStream::READ_OBJECT )
{
CATCH_EXCEPTION(is);
return ReadResult::FILE_NOT_HANDLED;
}
is.decompress(); CATCH_EXCEPTION(is);
osg::Object* obj = is.readObject(); CATCH_EXCEPTION(is);
return obj;
}
virtual ReadResult readImage( const std::string& file, const Options* options ) const
{
ReadResult result = ReadResult::FILE_LOADED;
std::string fileName = file;
Options* local_opt = prepareReading( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary );
return readImage( istream, local_opt );
} }
virtual ReadResult readImage( std::istream& fin, const Options* options ) const virtual ReadResult readImage( std::istream& fin, const Options* options ) const
@ -139,18 +197,13 @@ public:
virtual ReadResult readNode( const std::string& file, const Options* options ) const virtual ReadResult readNode( const std::string& file, const Options* options ) const
{ {
std::string ext = osgDB::getLowerCaseFileExtension( file ); ReadResult result = ReadResult::FILE_LOADED;
if ( !acceptsExtension(ext) ) return ReadResult::FILE_NOT_HANDLED; std::string fileName = file;
std::string fileName = osgDB::findDataFile( file, options ); Options* local_opt = prepareReading( result, fileName, options );
if ( fileName.empty() ) return ReadResult::FILE_NOT_FOUND; if ( !result.success() ) return result;
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary ); osgDB::ifstream istream( fileName.c_str(), std::ios::out|std::ios::binary );
return readNode( istream, local_opt.get() ); return readNode( istream, local_opt );
} }
virtual ReadResult readNode( std::istream& fin, const Options* options ) const virtual ReadResult readNode( std::istream& fin, const Options* options ) const
@ -170,41 +223,65 @@ public:
return node; return node;
} }
virtual WriteResult writeObject( const osg::Object& object, const std::string& fileName, const Options* options ) const Options* prepareWriting( WriteResult& result, const std::string& fileName, const Options* options ) const
{
const osg::Node* node = dynamic_cast<const osg::Node*>( &object );
if ( node ) return writeNode( *node, fileName, options );
const osg::Image* image = dynamic_cast<const osg::Image*>( &object );
if ( image ) return writeImage( *image, fileName, options );
return WriteResult::FILE_NOT_HANDLED;
}
virtual WriteResult writeObject( const osg::Object& object, std::ostream& fout, const Options* options ) const
{
const osg::Node* node = dynamic_cast<const osg::Node*>( &object );
if ( node ) return writeNode( *node, fout, options );
const osg::Image* image = dynamic_cast<const osg::Image*>( &object );
if ( image ) return writeImage( *image, fout, options );
return WriteResult::FILE_NOT_HANDLED;
}
virtual WriteResult writeImage( const osg::Image& image, const std::string& fileName, const Options* options ) const
{ {
std::string ext = osgDB::getFileExtension( fileName ); std::string ext = osgDB::getFileExtension( fileName );
if ( !acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED; if ( !acceptsExtension(ext) ) result = WriteResult::FILE_NOT_HANDLED;
osg::ref_ptr<Options> local_opt = options ? osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options; static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
if( local_opt->getDatabasePathList().empty() ) local_opt->getDatabasePathList().push_front(osgDB::getFilePath(fileName));
local_opt->setDatabasePath( osgDB::getFilePath(fileName) );
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" ); if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
if ( ext=="osgx" ) local_opt->setOptionString( local_opt->getOptionString() + " XML" );
return local_opt.release();
}
virtual WriteResult writeObject( const osg::Object& object, const std::string& fileName, const Options* options ) const
{
WriteResult result = WriteResult::FILE_SAVED;
Options* local_opt = prepareWriting( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary ); osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary );
if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE; if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;
WriteResult result = writeImage( image, fout, local_opt.get() ); result = writeObject( object, fout, local_opt );
fout.close();
return result;
}
virtual WriteResult writeObject( const osg::Object& object, std::ostream& fout, const Options* options ) const
{
osg::ref_ptr<OutputIterator> oi = writeInputIterator(fout, options);
OutputStream os( options );
os.start( oi.get(), OutputStream::WRITE_OBJECT ); CATCH_EXCEPTION(os);
os.writeObject( &object ); CATCH_EXCEPTION(os);
os.compress( &fout ); CATCH_EXCEPTION(os);
oi->flush();
if ( !os.getSchemaName().empty() )
{
osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
if ( !schemaStream.fail() ) os.writeSchema( schemaStream );
schemaStream.close();
}
if ( fout.fail() ) return WriteResult::ERROR_IN_WRITING_FILE;
return WriteResult::FILE_SAVED;
}
virtual WriteResult writeImage( const osg::Image& image, const std::string& fileName, const Options* options ) const
{
WriteResult result = WriteResult::FILE_SAVED;
Options* local_opt = prepareWriting( result, fileName, options );
if ( !result.success() ) return result;
osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary );
if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;
result = writeImage( image, fout, local_opt );
fout.close(); fout.close();
return result; return result;
} }
@ -218,6 +295,7 @@ public:
os.writeImage( &image ); CATCH_EXCEPTION(os); os.writeImage( &image ); CATCH_EXCEPTION(os);
os.compress( &fout ); CATCH_EXCEPTION(os); os.compress( &fout ); CATCH_EXCEPTION(os);
oi->flush();
if ( !os.getSchemaName().empty() ) if ( !os.getSchemaName().empty() )
{ {
osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out ); osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );
@ -231,19 +309,14 @@ public:
virtual WriteResult writeNode( const osg::Node& node, const std::string& fileName, const Options* options ) const virtual WriteResult writeNode( const osg::Node& node, const std::string& fileName, const Options* options ) const
{ {
std::string ext = osgDB::getFileExtension( fileName ); WriteResult result = WriteResult::FILE_SAVED;
if ( !acceptsExtension(ext) ) return WriteResult::FILE_NOT_HANDLED; Options* local_opt = prepareWriting( result, fileName, options );
if ( !result.success() ) return result;
osg::ref_ptr<Options> local_opt = options ?
static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
if ( local_opt->getDatabasePathList().empty() )
local_opt->setDatabasePath( osgDB::getFilePath(fileName) );
if ( ext=="osgt" ) local_opt->setOptionString( local_opt->getOptionString() + " Ascii" );
osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary ); osgDB::ofstream fout( fileName.c_str(), std::ios::out|std::ios::binary );
if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE; if ( !fout ) return WriteResult::ERROR_IN_WRITING_FILE;
WriteResult result = writeNode( node, fout, local_opt.get() ); result = writeNode( node, fout, local_opt );
fout.close(); fout.close();
return result; return result;
} }
@ -257,6 +330,7 @@ public:
os.writeObject( &node ); CATCH_EXCEPTION(os); os.writeObject( &node ); CATCH_EXCEPTION(os);
os.compress( &fout ); CATCH_EXCEPTION(os); os.compress( &fout ); CATCH_EXCEPTION(os);
oi->flush();
if ( !os.getSchemaName().empty() ) if ( !os.getSchemaName().empty() )
{ {
osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out ); osgDB::ofstream schemaStream( os.getSchemaName().c_str(), std::ios::out );

View File

@ -0,0 +1,510 @@
#ifndef OSGDB_XMLSTREAMOPERATOR
#define OSGDB_XMLSTREAMOPERATOR
#include <osgDB/StreamOperator>
#include <osgDB/XmlParser>
#include <sstream>
class XmlOutputIterator : public osgDB::OutputIterator
{
public:
enum ReadLineType
{
FIRST_LINE=0, // The first line of file
NEW_LINE, // A new line without checking its type
PROP_LINE, // A line starting with osgDB::PROPERTY
SUB_PROP_LINE, // A property line containing another osgDB::PROPERTY
BEGIN_BRACKET_LINE, // A line ending with a '{'
END_BRACKET_LINE, // A line starting with a '}'
TEXT_LINE // A text line, e.g. recording array elements
};
XmlOutputIterator( std::ostream* ostream )
: _readLineType(FIRST_LINE), _prevReadLineType(FIRST_LINE), _hasSubProperty(false)
{
_out = ostream;
_root = new osgDB::XmlNode;
_root->type = osgDB::XmlNode::GROUP;
}
virtual ~XmlOutputIterator() {}
virtual bool isBinary() const { return false; }
virtual void writeBool( bool b )
{ addToCurrentNode( b ? std::string("TRUE") : std::string("FALSE") ); }
virtual void writeChar( char c )
{ _sstream << c; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeUChar( unsigned char c )
{ _sstream << c; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeShort( short s )
{ _sstream << s; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeUShort( unsigned short s )
{ _sstream << s; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeInt( int i )
{ _sstream << i; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeUInt( unsigned int i )
{ _sstream << i; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeLong( long l )
{ _sstream << l; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeULong( unsigned long l )
{ _sstream << l; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeFloat( float f )
{ _sstream << f; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeDouble( double d )
{ _sstream << d; addToCurrentNode( _sstream.str() ); _sstream.str(""); }
virtual void writeString( const std::string& s )
{ addToCurrentNode( s, true ); }
virtual void writeStream( std::ostream& (*fn)(std::ostream&) )
{
if ( fn==static_cast<std::ostream& (*)(std::ostream&)>(std::endl) )
{
if ( _readLineType==PROP_LINE || _readLineType==END_BRACKET_LINE )
{
if ( _hasSubProperty )
{
_hasSubProperty = false;
popNode(); // Exit the sub-property element
}
popNode(); // Exit the property element
}
else if ( _readLineType==SUB_PROP_LINE )
{
_hasSubProperty = false;
popNode(); // Exit the sub-property element
popNode(); // Exit the property element
}
else if ( _readLineType==TEXT_LINE )
addToCurrentNode( fn );
setLineType( NEW_LINE );
}
else
addToCurrentNode( fn );
}
virtual void writeBase( std::ios_base& (*fn)(std::ios_base&) )
{
_sstream << fn;
}
virtual void writeGLenum( const osgDB::ObjectGLenum& value )
{
GLenum e = value.get();
const std::string& enumString = osgDB::Registry::instance()->getObjectWrapperManager()->getString("GL", e);
addToCurrentNode( enumString, true );
}
virtual void writeProperty( const osgDB::ObjectProperty& prop )
{
std::string enumString = prop._name;
if ( prop._mapProperty )
{
enumString = osgDB::Registry::instance()->getObjectWrapperManager()->getString(prop._name, prop._value);
addToCurrentNode( enumString, true );
}
else
{
if ( _readLineType==NEW_LINE || _readLineType==BEGIN_BRACKET_LINE )
{
pushNode( enumString );
setLineType( PROP_LINE );
}
else if ( _readLineType==PROP_LINE )
{
pushNode( enumString );
setLineType( SUB_PROP_LINE );
_hasSubProperty = true;
}
else if ( _readLineType==SUB_PROP_LINE )
{
popNode();
pushNode( enumString );
}
}
}
virtual void writeMark( const osgDB::ObjectMark& mark )
{
int delta = mark._indentDelta;
if ( delta>0 )
{
setLineType( BEGIN_BRACKET_LINE );
}
else if ( delta<0 )
{
setLineType( END_BRACKET_LINE );
}
}
virtual void writeCharArray( const char* s, unsigned int size ) {}
virtual void writeWrappedString( const std::string& str )
{
std::string realStr;
for ( std::string::const_iterator itr=str.begin(); itr!=str.end(); ++itr )
{
if ( *itr=='\"' )
realStr += "''";
else
realStr += *itr;
}
addToCurrentNode( realStr );
}
virtual void flush()
{
osg::ref_ptr<osgDB::XmlNode> xmlRoot = new osgDB::XmlNode;
xmlRoot->type = osgDB::XmlNode::ROOT;
xmlRoot->children.push_back( _root.get() );
xmlRoot->write( *_out );
}
protected:
void addToCurrentNode( const std::string& str, bool isString=false )
{
if ( _readLineType==FIRST_LINE )
{
_root->name = str;
return;
}
if ( _readLineType==NEW_LINE )
{
if ( isString )
{
pushNode( str );
setLineType( PROP_LINE );
return;
}
else
setLineType( TEXT_LINE );
}
if ( _readLineType==TEXT_LINE )
{
std::string& text = _nodePath.back()->properties["text"];
text += str + ' ';
}
else if ( _nodePath.size()>0 )
{
std::string& prop = _nodePath.back()->properties["attribute"];
if ( !prop.empty() ) prop += ' ';
prop += str;
}
else
{
pushNode( str );
setLineType( PROP_LINE );
}
}
void addToCurrentNode( std::ostream& (*fn)(std::ostream&) )
{
if ( _nodePath.size()>0 )
{
osgDB::XmlNode* node = _nodePath.back();
_sstream << fn;
if ( _readLineType==TEXT_LINE ) node->properties["text"] += _sstream.str();
else node->properties["attribute"] += _sstream.str();
_sstream.str("");
}
}
osgDB::XmlNode* pushNode( const std::string& name )
{
osg::ref_ptr<osgDB::XmlNode> node = new osgDB::XmlNode;
node->type = osgDB::XmlNode::ATOM;
// Set element name without '#' and '::' characters
std::string realName;
if ( name.length()>0 && name[0]=='#' )
realName = name.substr(1);
else
{
realName = name;
std::string::size_type pos = realName.find("::");
if ( pos!=std::string::npos )
realName.replace( pos, 2, "--" );
}
node->name = realName;
if ( _nodePath.size()>0 )
{
_nodePath.back()->type = osgDB::XmlNode::GROUP;
_nodePath.back()->children.push_back(node);
}
else
_root->children.push_back(node);
_nodePath.push_back( node.get() );
return node.get();
}
osgDB::XmlNode* popNode()
{
osgDB::XmlNode* node = NULL;
if ( _nodePath.size()>0 )
{
node = _nodePath.back();
trimEndMarkers( node, "attribute" );
trimEndMarkers( node, "text" );
_nodePath.pop_back();
}
return node;
}
void trimEndMarkers( osgDB::XmlNode* node, const std::string& name )
{
osgDB::XmlNode::Properties::iterator itr = node->properties.find(name);
if ( itr==node->properties.end() ) return;
std::string& str = itr->second;
if ( !str.empty() )
{
std::string::size_type end = str.find_last_not_of( " \t\r\n" );
if ( end==std::string::npos ) return;
str.erase( end+1 );
}
if ( str.empty() )
node->properties.erase(itr);
}
void setLineType( ReadLineType type )
{
_prevReadLineType = _readLineType;
_readLineType = type;
}
typedef std::vector<osgDB::XmlNode*> XmlNodePath;
XmlNodePath _nodePath;
osg::ref_ptr<osgDB::XmlNode> _root;
std::stringstream _sstream;
ReadLineType _readLineType;
ReadLineType _prevReadLineType;
bool _hasSubProperty;
};
class XmlInputIterator : public osgDB::InputIterator
{
public:
XmlInputIterator( std::istream* istream )
{
_in = istream;
_root = osgDB::readXmlStream( *istream );
if ( _root.valid() && _root->children.size()>0 )
_nodePath.push_back( _root->children[0] );
}
virtual ~XmlInputIterator() {}
virtual bool isBinary() const { return false; }
virtual void readBool( bool& b )
{
std::string boolString;
if ( prepareStream() ) _sstream >> boolString;
if ( boolString=="TRUE" ) b = true;
else b = false;
}
virtual void readChar( char& c )
{ if ( prepareStream() ) _sstream >> c; }
virtual void readSChar( signed char& c )
{ if ( prepareStream() ) _sstream >> c; }
virtual void readUChar( unsigned char& c )
{ if ( prepareStream() ) _sstream >> c; }
virtual void readShort( short& s )
{ if ( prepareStream() ) _sstream >> s; }
virtual void readUShort( unsigned short& s )
{ if ( prepareStream() ) _sstream >> s; }
virtual void readInt( int& i )
{ if ( prepareStream() ) _sstream >> i; }
virtual void readUInt( unsigned int& i )
{ if ( prepareStream() ) _sstream >> i; }
virtual void readLong( long& l )
{ if ( prepareStream() ) _sstream >> l; }
virtual void readULong( unsigned long& l )
{ if ( prepareStream() ) _sstream >> l; }
virtual void readFloat( float& f )
{ if ( prepareStream() ) _sstream >> f; }
virtual void readDouble( double& d )
{ if ( prepareStream() ) _sstream >> d; }
virtual void readString( std::string& s )
{
if ( prepareStream() ) _sstream >> s;
// Replace '--' to '::' to get correct wrapper class
std::string::size_type pos = s.find("--");
if ( pos!=std::string::npos )
s.replace( pos, 2, "::" );
}
virtual void readStream( std::istream& (*fn)(std::istream&) )
{ if ( prepareStream() ) _sstream >> fn; }
virtual void readBase( std::ios_base& (*fn)(std::ios_base&) )
{ _sstream >> fn; }
virtual void readGLenum( osgDB::ObjectGLenum& value )
{
GLenum e = 0;
std::string enumString;
if ( prepareStream() ) _sstream >> enumString;
e = osgDB::Registry::instance()->getObjectWrapperManager()->getValue("GL", enumString);
value.set( e );
}
virtual void readProperty( osgDB::ObjectProperty& prop )
{
int value = 0;
std::string enumString;
if ( prepareStream() ) _sstream >> enumString;
if ( prop._mapProperty )
{
value = osgDB::Registry::instance()->getObjectWrapperManager()->getValue(prop._name, enumString);
}
else
{
// Replace '--' to '::' to get correct wrapper class
std::string::size_type pos = enumString.find("--");
if ( pos!=std::string::npos )
enumString.replace( pos, 2, "::" );
if ( prop._name!=enumString )
{
if ( prop._name[0]=='#' )
enumString = '#' + enumString;
if ( prop._name!=enumString )
{
OSG_NOTIFY(osg::WARN) << "XmlInputIterator::readProperty(): Unmatched property "
<< enumString << ", expecting " << prop._name << std::endl;
}
}
prop._name = enumString;
}
prop.set( value );
}
virtual void readMark( osgDB::ObjectMark& mark ) {}
virtual void readCharArray( char* s, unsigned int size ) {}
virtual void readWrappedString( std::string& str )
{
std::string realStr;
if ( prepareStream() ) std::getline( _sstream, realStr );
for ( std::string::const_iterator itr=realStr.begin(); itr!=realStr.end(); ++itr )
{
if ( *itr=='\'' )
{
itr++;
if ( itr==realStr.end() ) break;
if ( *itr=='\'' ) str += '\"';
else str += '\'' + *itr;
}
else
str += *itr;
}
}
virtual bool matchString( const std::string& str )
{
prepareStream();
unsigned int size = str.length();
if ( _sstream.str().length()<size ) return false;
int result = _sstream.str().compare( 0, size, str );
if ( !result )
{
std::string prop; readString( prop );
return true;
}
return false;
}
virtual void advanceToCurrentEndBracket() {}
protected:
bool isReadable() const { return _sstream.rdbuf()->in_avail()>0; }
bool prepareStream()
{
if ( !_nodePath.size() ) return false;
if ( isReadable() ) return true;
_sstream.clear();
osgDB::XmlNode* current = _nodePath.back();
if ( !current->name.empty() )
{
_sstream.str( current->name );
current->name.clear();
return true;
}
if ( current->properties.size()>0 )
{
if ( applyPropertyToStream(current, "attribute") ) return true;
else if ( applyPropertyToStream(current, "text") ) return true;
}
if ( current->children.size()>0 )
{
_nodePath.push_back( current->children.front() );
current->children.erase( current->children.begin() );
return prepareStream();
}
_nodePath.pop_back();
return prepareStream();
}
bool applyPropertyToStream( osgDB::XmlNode* node, const std::string& name )
{
osgDB::XmlNode::Properties::iterator itr = node->properties.find(name);
if ( itr!=node->properties.end() )
{
_sstream.str( itr->second );
node->properties.erase( itr );
return true;
}
return false;
}
typedef std::vector< osg::ref_ptr<osgDB::XmlNode> > XmlNodePath;
XmlNodePath _nodePath;
osg::ref_ptr<osgDB::XmlNode> _root;
std::stringstream _sstream;
};
#endif