/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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. */ // Written by Wang Rui, (C) 2010 #include #include #include #include #include #include using namespace osgDB; OutputStream::OutputStream( const osgDB::Options* options ) : _writeImageHint(WRITE_USE_IMAGE_HINT), _useSchemaData(false) { if ( !options ) return; _options = options; StringList optionList; split( options->getOptionString(), optionList ); for ( StringList::iterator itr=optionList.begin(); itr!=optionList.end(); ++itr ) { const std::string& option = *itr; if ( option=="Ascii" ) { // Omit this } else if ( option=="SchemaData" ) { _useSchemaData = true; } else { StringList keyAndValues; split( option, keyAndValues, '=' ); if ( keyAndValues.size()<2 ) continue; if ( keyAndValues[0]=="SchemaFile" ) _schemaName = keyAndValues[1]; else if ( keyAndValues[0]=="Compressor" ) _compressorName = keyAndValues[1]; else if ( keyAndValues[0]=="WriteImageHint" ) { if ( keyAndValues[1]=="IncludeData" ) _writeImageHint = WRITE_INLINE_DATA; else if ( keyAndValues[1]=="IncludeFile" ) _writeImageHint = WRITE_INLINE_FILE; else if ( keyAndValues[1]=="UseExternal" ) _writeImageHint = WRITE_USE_EXTERNAL; else if ( keyAndValues[1]=="WriteOut" ) _writeImageHint = WRITE_EXTERNAL_FILE; } else osg::notify(osg::WARN) << "OutputStream: Unknown option " << option << std::endl; } } } OutputStream::~OutputStream() { } OutputStream& OutputStream::operator<<( const osg::Vec2b& v ) { *this << v.x() << v.y(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec3b& v ) { *this << v.x() << v.y() << v.z(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec4b& v ) { *this << v.x() << v.y() << v.z() << v.w(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec4ub& v ) { *this << v.r() << v.g() << v.b() << v.a(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec2s& v ) { *this << v.x() << v.y(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec3s& v ) { *this << v.x() << v.y() << v.z(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec4s& v ) { *this << v.x() << v.y() << v.z() << v.w(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec2f& v ) { *this << v.x() << v.y(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec3f& v ) { *this << v.x() << v.y() << v.z(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec4f& v ) { *this << v.x() << v.y() << v.z() << v.w(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec2d& v ) { *this << v.x() << v.y(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec3d& v ) { *this << v.x() << v.y() << v.z(); return *this; } OutputStream& OutputStream::operator<<( const osg::Vec4d& v ) { *this << v.x() << v.y() << v.z() << v.w(); return *this; } OutputStream& OutputStream::operator<<( const osg::Quat& q ) { *this << q.x() << q.y() << q.z() << q.w(); return *this; } OutputStream& OutputStream::operator<<( const osg::Plane& p ) { *this << (double)p[0] << (double)p[1] << (double)p[2] << (double)p[3]; return *this; } OutputStream& OutputStream::operator<<( const osg::Matrixf& mat ) { *this << PROPERTY("Matrixf") << BEGIN_BRACKET << std::endl; for ( int r=0; r<4; ++r ) { *this << mat(r, 0) << mat(r, 1) << mat(r, 2) << mat(r, 3) << std::endl; } *this << END_BRACKET << std::endl; return *this; } OutputStream& OutputStream::operator<<( const osg::Matrixd& mat ) { *this << PROPERTY("Matrixd") << BEGIN_BRACKET << std::endl; for ( int r=0; r<4; ++r ) { *this << mat(r, 0) << mat(r, 1) << mat(r, 2) << mat(r, 3) << std::endl; } *this << END_BRACKET << std::endl; return *this; } void OutputStream::writeArray( const osg::Array* a ) { if ( !a ) return; unsigned int id = findOrCreateArrayID( a ); *this << PROPERTY("ArrayID") << id; if ( id<_arrayMap.size() ) // Shared array { *this << std::endl; return; } switch ( a->getType() ) { case osg::Array::ByteArrayType: *this << MAPPEE(ArrayType, ID_BYTE_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::UByteArrayType: *this << MAPPEE(ArrayType, ID_UBYTE_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::ShortArrayType: *this << MAPPEE(ArrayType, ID_SHORT_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::UShortArrayType: *this << MAPPEE(ArrayType, ID_USHORT_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::IntArrayType: *this << MAPPEE(ArrayType, ID_INT_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::UIntArrayType: *this << MAPPEE(ArrayType, ID_UINT_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::FloatArrayType: *this << MAPPEE(ArrayType, ID_FLOAT_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::DoubleArrayType: *this << MAPPEE(ArrayType, ID_DOUBLE_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements(), 4 ); break; case osg::Array::Vec2bArrayType: *this << MAPPEE(ArrayType, ID_VEC2B_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec3bArrayType: *this << MAPPEE(ArrayType, ID_VEC3B_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec4bArrayType: *this << MAPPEE(ArrayType, ID_VEC4B_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec4ubArrayType: *this << MAPPEE(ArrayType, ID_VEC4UB_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec2sArrayType: *this << MAPPEE(ArrayType, ID_VEC2S_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec3sArrayType: *this << MAPPEE(ArrayType, ID_VEC3S_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec4sArrayType: *this << MAPPEE(ArrayType, ID_VEC4S_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec2ArrayType: *this << MAPPEE(ArrayType, ID_VEC2_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec3ArrayType: *this << MAPPEE(ArrayType, ID_VEC3_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec4ArrayType: *this << MAPPEE(ArrayType, ID_VEC4_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec2dArrayType: *this << MAPPEE(ArrayType, ID_VEC4D_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec3dArrayType: *this << MAPPEE(ArrayType, ID_VEC4D_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; case osg::Array::Vec4dArrayType: *this << MAPPEE(ArrayType, ID_VEC4D_ARRAY); writeArrayImplementation( static_cast(a), a->getNumElements() ); break; default: throwException( "OutputStream::writeArray(): Unsupported array type." ); } } void OutputStream::writePrimitiveSet( const osg::PrimitiveSet* p ) { if ( !p ) return; switch ( p->getType() ) { case osg::PrimitiveSet::DrawArraysPrimitiveType: *this << MAPPEE(PrimitiveType, ID_DRAWARRAYS); { const osg::DrawArrays* da = static_cast(p); *this << MAPPEE(PrimitiveType, da->getMode()) << da->getFirst() << da->getCount() << std::endl; } break; case osg::PrimitiveSet::DrawArrayLengthsPrimitiveType: *this << MAPPEE(PrimitiveType, ID_DRAWARRAY_LENGTH); { const osg::DrawArrayLengths* dl = static_cast(p); *this << MAPPEE(PrimitiveType, dl->getMode()) << dl->getFirst(); writeArrayImplementation( dl, dl->size(), 4 ); } break; case osg::PrimitiveSet::DrawElementsUBytePrimitiveType: *this << MAPPEE(PrimitiveType, ID_DRAWELEMENTS_UBYTE); { const osg::DrawElementsUByte* de = static_cast(p); *this << MAPPEE(PrimitiveType, de->getMode()); writeArrayImplementation( de, de->size(), 4 ); } break; case osg::PrimitiveSet::DrawElementsUShortPrimitiveType: *this << MAPPEE(PrimitiveType, ID_DRAWELEMENTS_USHORT); { const osg::DrawElementsUShort* de = static_cast(p); *this << MAPPEE(PrimitiveType, de->getMode()); writeArrayImplementation( de, de->size(), 4 ); } break; case osg::PrimitiveSet::DrawElementsUIntPrimitiveType: *this << MAPPEE(PrimitiveType, ID_DRAWELEMENTS_UINT); { const osg::DrawElementsUInt* de = static_cast(p); *this << MAPPEE(PrimitiveType, de->getMode()); writeArrayImplementation( de, de->size(), 4 ); } break; default: throwException( "OutputStream::writePrimitiveSet(): Unsupported primitive type." ); } } void OutputStream::writeImage( const osg::Image* img ) { 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("WriteHint") << (int)img->getWriteHint(); if ( getException() ) return; int decision = IMAGE_EXTERNAL; switch ( _writeImageHint ) { case OutputStream::WRITE_INLINE_DATA: decision = IMAGE_INLINE_DATA; break; case OutputStream::WRITE_INLINE_FILE: decision = IMAGE_INLINE_FILE; break; case OutputStream::WRITE_EXTERNAL_FILE: decision = IMAGE_EXTERNAL; break; case OutputStream::WRITE_USE_EXTERNAL: decision = IMAGE_WRITE_OUT; break; default: if ( img->getWriteHint()==osg::Image::EXTERNAL_FILE ) decision = IMAGE_EXTERNAL; else if ( isBinary() ) decision = IMAGE_INLINE_DATA; break; } *this << decision << std::endl; if ( decision==IMAGE_WRITE_OUT || _writeImageHint==WRITE_EXTERNAL_FILE ) { bool result = osgDB::writeImageFile( *img, img->getFileName() ); osg::notify(osg::NOTICE) << "OutputStream::writeImage(): Write image data to external file " << img->getFileName() << std::endl; if ( !result ) { osg::notify(osg::WARN) << "OutputStream::writeImage(): Failed to write " << img->getFileName() << std::endl; } } switch ( decision ) { case IMAGE_INLINE_DATA: if ( isBinary() ) { *this << img->getOrigin(); // _origin *this << img->s() << img->t() << img->r(); // _s & _t & _r *this << img->getInternalTextureFormat(); // _internalTextureFormat *this << img->getPixelFormat(); // _pixelFormat *this << img->getDataType(); // _dataType *this << img->getPacking(); // _packing *this << img->getAllocationMode(); // _allocationMode // _data unsigned int size = img->getTotalSizeInBytesIncludingMipmaps(); writeSize(size); writeCharArray( (char*)img->data(), size ); // _mipmapData const osg::Image::MipmapDataType& levels = img->getMipmapLevels(); writeSize(levels.size()); for ( osg::Image::MipmapDataType::const_iterator itr=levels.begin(); itr!=levels.end(); ++itr ) { *this << *itr; } } break; case IMAGE_INLINE_FILE: if ( isBinary() ) { std::string fullPath = osgDB::findDataFile( img->getFileName() ); std::ifstream infile( fullPath.c_str(), std::ios::in|std::ios::binary ); if ( infile ) { infile.seekg( 0, std::ios::end ); unsigned int size = infile.tellg(); writeSize(size); if ( size>0 ) { char* data = new char[size]; if ( !data ) throwException( "OutputStream::writeImage(): Out of memory." ); if ( getException() ) return; infile.seekg( 0, std::ios::beg ); infile.read( data, size ); writeCharArray( data, size ); delete[] data; } infile.close(); } else { osg::notify(osg::WARN) << "OutputStream::writeImage(): Failed to open image file " << img->getFileName() << std::endl; *this << (unsigned int)0; } } break; case IMAGE_EXTERNAL: break; default: break; } writeObject( img ); } void OutputStream::writeObject( const osg::Object* obj ) { if ( !obj ) return; std::string name = obj->libraryName(); name += std::string("::") + obj->className(); unsigned int id = findOrCreateObjectID( obj ); *this << name << BEGIN_BRACKET << std::endl; // Write object name *this << PROPERTY("UniqueID") << id << std::endl; // Write object ID if ( getException() ) return; // Check whether this is a shared object or not if ( id>=_objectMap.size() ) { ObjectWrapper* wrapper = Registry::instance()->getObjectWrapperManager()->findWrapper( name ); if ( !wrapper ) { osg::notify(osg::WARN) << "OutputStream::writeObject(): Unsupported wrapper class " << name << std::endl; *this << END_BRACKET << std::endl; return; } _fields.push_back( name ); const StringList& associates = wrapper->getAssociates(); for ( StringList::const_iterator itr=associates.begin(); itr!=associates.end(); ++itr ) { const std::string& assocName = *itr; ObjectWrapper* assocWrapper = Registry::instance()->getObjectWrapperManager()->findWrapper(assocName); if ( !assocWrapper ) { osg::notify(osg::WARN) << "OutputStream::writeObject(): Unsupported associated class " << assocName << std::endl; continue; } else if ( _useSchemaData ) { if ( _inbuiltSchemaMap.find(assocName)==_inbuiltSchemaMap.end() ) { StringList properties; assocWrapper->writeSchema( properties ); if ( properties.size()>0 ) { std::string propertiesString; for ( StringList::iterator sitr=properties.begin(); sitr!=properties.end(); ++sitr ) { propertiesString += *sitr; propertiesString += ' '; } _inbuiltSchemaMap[assocName] = propertiesString; } } } _fields.push_back( assocWrapper->getName() ); assocWrapper->write( *this, *obj ); if ( getException() ) return; _fields.pop_back(); } _fields.pop_back(); } *this << END_BRACKET << std::endl; } void OutputStream::start( OutputIterator* outIterator, OutputStream::WriteType type ) { _fields.clear(); _fields.push_back( "Start" ); _out = outIterator; if ( !_out ) throwException( "OutputStream: Null stream specified." ); if ( getException() ) return; if ( isBinary() ) { *this << (unsigned int)type << (unsigned int)PLUGIN_VERSION; bool useCompressSource = false; unsigned int attributes = 0; if ( sizeof(osg::Matrix::value_type)!=FLOAT_SIZE ) attributes |= 0x1; // Record matrix value type of current built OSG if ( _useSchemaData ) { attributes |= 0x2; // Record if we use inbuilt schema data or not useCompressSource = true; } *this << attributes; if ( !_compressorName.empty() ) { BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(_compressorName); if ( !compressor ) { osg::notify(osg::WARN) << "OutputStream::start(): No such compressor " << _compressorName << std::endl; _compressorName.clear(); } else { useCompressSource = true; } } if ( !_compressorName.empty() ) *this << _compressorName; else *this << std::string("0"); // No compressor // Compressors and inbuilt schema use a new stream, which will be merged with the original one at the end. if ( useCompressSource ) { _out->flush(); _out->setStream( &_compressSource ); } } else { std::string typeString("Unknown"); switch ( type ) { case WRITE_SCENE: typeString = "Scene"; break; case WRITE_IMAGE: typeString = "Image"; break; case WRITE_OBJECT: typeString = "Object"; break; default: break; } *this << typeString << std::endl; *this << PROPERTY("#Version") << (unsigned int)PLUGIN_VERSION << std::endl; *this << PROPERTY("#Generator") << std::string("OpenSceneGraph") << std::string(osgGetVersion()) << std::endl; *this << std::endl; } _fields.pop_back(); } void OutputStream::compress( std::ostream* ostream ) { _fields.clear(); if ( !isBinary() ) return; std::stringstream schemaSource; if ( _useSchemaData ) { _fields.push_back( "SchemaData" ); std::string schemaData; for ( std::map::iterator itr=_inbuiltSchemaMap.begin(); itr!=_inbuiltSchemaMap.end(); ++itr ) { schemaData += itr->first + '='; schemaData += itr->second; schemaData += '\n'; } int size = schemaData.size(); schemaSource.write( (char*)&size, INT_SIZE ); schemaSource.write( schemaData.c_str(), size ); _inbuiltSchemaMap.clear(); _fields.pop_back(); } if ( !_compressorName.empty() ) { _fields.push_back( "Compression" ); BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(_compressorName); if ( !compressor || !ostream ) { _fields.pop_back(); return; } if ( !compressor->compress(*ostream, schemaSource.str() + _compressSource.str()) ) throwException( "OutputStream: Failed to compress stream." ); if ( getException() ) return; _fields.pop_back(); } else if ( _useSchemaData ) { std::string str = schemaSource.str() + _compressSource.str(); ostream->write( str.c_str(), str.size() ); } } void OutputStream::writeSchema( std::ostream& fout ) { // Write to external ascii stream const ObjectWrapperManager::WrapperMap& wrappers = Registry::instance()->getObjectWrapperManager()->getWrapperMap(); for ( ObjectWrapperManager::WrapperMap::const_iterator itr=wrappers.begin(); itr!=wrappers.end(); ++itr ) { ObjectWrapper* wrapper = itr->second.get(); fout << itr->first << " ="; StringList properties; wrapper->writeSchema( properties ); if ( properties.size()>0 ) { for ( StringList::iterator sitr=properties.begin(); sitr!=properties.end(); ++sitr ) { fout << ' ' << *sitr; } } fout << std::endl; } } // PROTECTED METHODS template void OutputStream::writeArrayImplementation( const T* a, int write_size, unsigned int numInRow ) { *this << write_size << BEGIN_BRACKET; if ( numInRow>1 ) { for ( int i=0; isecond; } unsigned int OutputStream::findOrCreateObjectID( const osg::Object* obj ) { ObjectMap::iterator itr = _objectMap.find( obj ); if ( itr==_objectMap.end() ) { unsigned int id = _objectMap.size()+1; _objectMap[obj] = id; return id; } return itr->second; }