/* -*-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; static std::string s_lastSchema; InputStream::InputStream( const osgDB::Options* options ) : _fileVersion(0), _byteSwap(0), _useSchemaData(false), _forceReadingImage(false), _dataDecompress(0) { if ( !options ) return; _options = options; std::string schema; if ( options->getPluginStringData("ForceReadingImage")=="true" ) _forceReadingImage = true; if ( !options->getPluginStringData("SchemaFile").empty() ) { schema = options->getPluginStringData("SchemaFile"); if ( s_lastSchema!=schema ) { osgDB::ifstream schemaStream( schema.c_str(), std::ios::in ); if ( !schemaStream.fail() ) readSchema( schemaStream ); schemaStream.close(); s_lastSchema = schema; } } if ( schema.empty() ) { resetSchema(); s_lastSchema.clear(); } } InputStream::~InputStream() { if (_dataDecompress) delete _dataDecompress; } InputStream& InputStream::operator>>( osg::Vec2b& v ) { char x, y; *this >> x >> y; v.set( x, y ); return *this; } InputStream& InputStream::operator>>( osg::Vec3b& v ) { char x, y, z; *this >> x >> y >> z; v.set( x, y, z ); return *this; } InputStream& InputStream::operator>>( osg::Vec4b& v ) { char x, y, z, w; *this >> x >> y >> z >> w; v.set( x, y, z, w ); return *this; } InputStream& InputStream::operator>>( osg::Vec4ub& v ) { char r, g, b, a; *this >> r >> g >> b >> a; v.set( r, g, b, a ); return *this; } InputStream& InputStream::operator>>( osg::Vec2s& v ) { *this >> v.x() >> v.y(); return *this; } InputStream& InputStream::operator>>( osg::Vec3s& v ) { *this >> v.x() >> v.y() >> v.z(); return *this; } InputStream& InputStream::operator>>( osg::Vec4s& v ) { *this >> v.x() >> v.y() >> v.z() >> v.w(); return *this; } InputStream& InputStream::operator>>( osg::Vec2f& v ) { *this >> v.x() >> v.y(); return *this; } InputStream& InputStream::operator>>( osg::Vec3f& v ) { *this >> v.x() >> v.y() >> v.z(); return *this; } InputStream& InputStream::operator>>( osg::Vec4f& v ) { *this >> v.x() >> v.y() >> v.z() >> v.w(); return *this; } InputStream& InputStream::operator>>( osg::Vec2d& v ) { *this >> v.x() >> v.y(); return *this; } InputStream& InputStream::operator>>( osg::Vec3d& v ) { *this >> v.x() >> v.y() >> v.z(); return *this; } InputStream& InputStream::operator>>( osg::Vec4d& v ) { *this >> v.x() >> v.y() >> v.z() >> v.w(); return *this; } InputStream& InputStream::operator>>( osg::Quat& q ) { *this >> q.x() >> q.y() >> q.z() >> q.w(); return *this; } InputStream& InputStream::operator>>( osg::Plane& p ) { double p0, p1, p2, p3; *this >> p0 >> p1 >> p2 >> p3; p.set( p0, p1, p2, p3 ); return *this; } #if 0 InputStream& InputStream::operator>>( osg::Matrixf& mat ) { ObjectProperty property(""); *this >> property >> BEGIN_BRACKET; if (property._name == "Matrixf") { // stream has same type as what we want to read so read directly for ( int r=0; r<4; ++r ) { *this >> mat(r, 0) >> mat(r, 1) >> mat(r, 2) >> mat(r, 3); } } else if (property._name == "Matrixd") { // stream has different type than what we want to read so read stream into // a temporary and then copy across to the final matrix double value; for ( int r=0; r<4; ++r ) { for ( int c=0; c<4; ++c) { *this >> value; mat(r,c) = static_cast(value); } } } *this >> END_BRACKET; return *this; } InputStream& InputStream::operator>>( osg::Matrixd& mat ) { ObjectProperty property(""); *this >> property >> BEGIN_BRACKET; if (property._name == "Matrixf") { // stream has different type than what we want to read so read stream into // a temporary and then copy across to the final matrix float value; for ( int r=0; r<4; ++r ) { for ( int c=0; c<4; ++c) { *this >> value; mat(r,c) = static_cast(value); } } } else if (property._name == "Matrixd") { // stream has same type as what we want to read so read directly for ( int r=0; r<4; ++r ) { *this >> mat(r, 0) >> mat(r, 1) >> mat(r, 2) >> mat(r, 3); } } *this >> END_BRACKET; return *this; } #else InputStream& InputStream::operator>>( osg::Matrixf& mat ) { *this >> BEGIN_BRACKET; // stream has different type than what we want to read so read stream into // a temporary and then copy across to the final matrix double value; for ( int r=0; r<4; ++r ) { for ( int c=0; c<4; ++c) { *this >> value; mat(r,c) = static_cast(value); } } *this >> END_BRACKET; return *this; } InputStream& InputStream::operator>>( osg::Matrixd& mat ) { *this >> BEGIN_BRACKET; for ( int r=0; r<4; ++r ) { *this >> mat(r, 0) >> mat(r, 1) >> mat(r, 2) >> mat(r, 3); } *this >> END_BRACKET; return *this; } #endif osg::Array* InputStream::readArray() { osg::ref_ptr array = NULL; unsigned int id = 0; *this >> PROPERTY("ArrayID") >> id; ArrayMap::iterator itr = _arrayMap.find( id ); if ( itr!=_arrayMap.end() ) { return itr->second.get(); } DEF_MAPPEE(ArrayType, type); *this >> type; switch ( type.get() ) { case ID_BYTE_ARRAY: { osg::ByteArray* ba = new osg::ByteArray; readArrayImplementation( ba, CHAR_SIZE, true ); array = ba; } break; case ID_UBYTE_ARRAY: { osg::UByteArray* uba = new osg::UByteArray; readArrayImplementation( uba, CHAR_SIZE, true ); array = uba; } break; case ID_SHORT_ARRAY: { osg::ShortArray* sa = new osg::ShortArray; readArrayImplementation( sa, SHORT_SIZE, true ); array = sa; } break; case ID_USHORT_ARRAY: { osg::UShortArray* usa = new osg::UShortArray; readArrayImplementation( usa, SHORT_SIZE, true ); array = usa; } break; case ID_INT_ARRAY: { osg::IntArray* ia = new osg::IntArray; readArrayImplementation( ia, INT_SIZE, true ); array = ia; } break; case ID_UINT_ARRAY: { osg::UIntArray* uia = new osg::UIntArray; readArrayImplementation( uia, INT_SIZE, true ); array = uia; } break; case ID_FLOAT_ARRAY: { osg::FloatArray* fa = new osg::FloatArray; readArrayImplementation( fa, FLOAT_SIZE, true ); array = fa; } break; case ID_DOUBLE_ARRAY: { osg::DoubleArray* da = new osg::DoubleArray; readArrayImplementation( da, DOUBLE_SIZE, true ); array = da; } break; case ID_VEC2B_ARRAY: { osg::Vec2bArray* va = new osg::Vec2bArray; readArrayImplementation( va, 2*CHAR_SIZE ); array = va; } break; case ID_VEC3B_ARRAY: { osg::Vec3bArray* va = new osg::Vec3bArray; readArrayImplementation( va, 3*CHAR_SIZE ); array = va; } break; case ID_VEC4B_ARRAY: { osg::Vec4bArray* va = new osg::Vec4bArray; readArrayImplementation( va, 4*CHAR_SIZE ); array = va; } break; case ID_VEC4UB_ARRAY: { osg::Vec4ubArray* va = new osg::Vec4ubArray; readArrayImplementation( va, 4*CHAR_SIZE ); array = va; } break; case ID_VEC2S_ARRAY: { osg::Vec2sArray* va = new osg::Vec2sArray; readArrayImplementation( va, 2*SHORT_SIZE ); array = va; } break; case ID_VEC3S_ARRAY: { osg::Vec3sArray* va = new osg::Vec3sArray; readArrayImplementation( va, 3*SHORT_SIZE ); array = va; } break; case ID_VEC4S_ARRAY: { osg::Vec4sArray* va = new osg::Vec4sArray; readArrayImplementation( va, 4*SHORT_SIZE ); array = va; } break; case ID_VEC2_ARRAY: { osg::Vec2Array* va = new osg::Vec2Array; readArrayImplementation( va, 2*FLOAT_SIZE ); array = va; } break; case ID_VEC3_ARRAY: { osg::Vec3Array* va = new osg::Vec3Array; readArrayImplementation( va, 3*FLOAT_SIZE ); array = va; } break; case ID_VEC4_ARRAY: { osg::Vec4Array* va = new osg::Vec4Array; readArrayImplementation( va, 4*FLOAT_SIZE ); array = va; } break; case ID_VEC2D_ARRAY: { osg::Vec2dArray* va = new osg::Vec2dArray; readArrayImplementation( va, 2*DOUBLE_SIZE ); array = va; } break; case ID_VEC3D_ARRAY: { osg::Vec3dArray* va = new osg::Vec3dArray; readArrayImplementation( va, 3*DOUBLE_SIZE ); array = va; } break; case ID_VEC4D_ARRAY: { osg::Vec4dArray* va = new osg::Vec4dArray; readArrayImplementation( va, 4*DOUBLE_SIZE ); array = va; } break; default: throwException( "InputStream::readArray(): Unsupported array type." ); } if ( getException() ) return NULL; _arrayMap[id] = array; return array.release(); } osg::PrimitiveSet* InputStream::readPrimitiveSet() { osg::ref_ptr primitive = NULL; DEF_MAPPEE(PrimitiveType, type); DEF_MAPPEE(PrimitiveType, mode); *this >> type >> mode; switch ( type.get() ) { case ID_DRAWARRAYS: { int first = 0, count = 0; *this >> first >> count; osg::DrawArrays* da = new osg::DrawArrays( mode.get(), first, count ); primitive = da; } break; case ID_DRAWARRAY_LENGTH: { int first = 0, value = 0; unsigned int size = 0; *this >> first >> size >> BEGIN_BRACKET; osg::DrawArrayLengths* dl = new osg::DrawArrayLengths( mode.get(), first ); for ( unsigned int i=0; i> value; dl->push_back( value ); } *this >> END_BRACKET; primitive = dl; } break; case ID_DRAWELEMENTS_UBYTE: { osg::DrawElementsUByte* de = new osg::DrawElementsUByte( mode.get() ); unsigned int size = 0; unsigned char value = 0; *this >> size >> BEGIN_BRACKET; for ( unsigned int i=0; i> value; de->push_back( value ); } *this >> END_BRACKET; primitive = de; } break; case ID_DRAWELEMENTS_USHORT: { osg::DrawElementsUShort* de = new osg::DrawElementsUShort( mode.get() ); unsigned int size = 0; unsigned short value = 0; *this >> size >> BEGIN_BRACKET; for ( unsigned int i=0; i> value; de->push_back( value ); } *this >> END_BRACKET; primitive = de; } break; case ID_DRAWELEMENTS_UINT: { osg::DrawElementsUInt* de = new osg::DrawElementsUInt( mode.get() ); unsigned int size = 0, value = 0; *this >> size >> BEGIN_BRACKET; for ( unsigned int i=0; i> value; de->push_back( value ); } *this >> END_BRACKET; primitive = de; } break; default: throwException( "InputStream::readPrimitiveSet(): Unsupported array type." ); } if ( getException() ) return NULL; return primitive.release(); } osg::Image* InputStream::readImage(bool readFromExternal) { std::string className="osg::Image"; unsigned int id = 0; *this >> PROPERTY("UniqueID") >> id; if ( getException() ) return NULL; IdentifierMap::iterator itr = _identifierMap.find( id ); if ( itr!=_identifierMap.end() ) { return static_cast( itr->second.get() ); } std::string name; int writeHint, decision = IMAGE_EXTERNAL; *this >> PROPERTY("FileName"); readWrappedString(name); *this >> PROPERTY("WriteHint") >> writeHint >> decision; if ( getException() ) return NULL; osg::ref_ptr image = NULL; switch ( decision ) { case IMAGE_INLINE_DATA: if ( isBinary() ) { image = new osg::Image; // _origin, _s & _t & _r, _internalTextureFormat int origin, s, t, r, internalFormat; *this >> origin >> s >> t >> r >> internalFormat; // _pixelFormat, _dataType, _packing, _allocationMode int pixelFormat, dataType, packing, mode; *this >> pixelFormat >> dataType >> packing >> mode; // _data unsigned int size = 0; *this >> size; if ( size ) { char* data = new char[size]; if ( !data ) throwException( "InputStream::readImage() Out of memory." ); if ( getException() ) return NULL; readCharArray( data, size ); image->setOrigin( (osg::Image::Origin)origin ); image->setImage( s, t, r, internalFormat, pixelFormat, dataType, (unsigned char*)data, (osg::Image::AllocationMode)mode, packing ); } // _mipmapData unsigned int levelSize = readSize(); osg::Image::MipmapDataType levels(levelSize); for ( unsigned int i=0; i> levels[i]; } if ( levelSize>0 ) image->setMipmapLevels( levels ); readFromExternal = false; } break; case IMAGE_INLINE_FILE: if ( isBinary() ) { unsigned int size = readSize(); if ( size>0 ) { char* data = new char[size]; if ( !data ) { throwException( "InputStream::readImage(): Out of memory." ); if ( getException() ) return NULL; } readCharArray( data, size ); std::string ext = osgDB::getFileExtension( name ); osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension( ext ); if ( reader ) { std::stringstream inputStream; inputStream.write( data, size ); osgDB::ReaderWriter::ReadResult rr = reader->readImage( inputStream ); if ( rr.validImage() ) image = rr.takeImage(); else { OSG_WARN << "InputStream::readImage(): " << rr.message() << std::endl; } } else { OSG_WARN << "InputStream::readImage(): Unable to find a plugin for " << ext << std::endl; } delete[] data; } readFromExternal = false; } break; case IMAGE_EXTERNAL: case IMAGE_WRITE_OUT: break; default: break; } if ( readFromExternal ) { image = osgDB::readImageFile( name, getOptions() ); if ( !image && _forceReadingImage ) image = new osg::Image; } if ( image.valid() ) { image->setFileName( name ); image->setWriteHint( (osg::Image::WriteHint)writeHint ); } image = static_cast( readObjectFields(className, image.get()) ); _identifierMap[id] = image; return image.release(); } osg::Object* InputStream::readObject( osg::Object* existingObj ) { std::string className; unsigned int id = 0; *this >> className >> BEGIN_BRACKET >> PROPERTY("UniqueID") >> id; if ( getException() ) return NULL; IdentifierMap::iterator itr = _identifierMap.find( id ); if ( itr!=_identifierMap.end() ) { advanceToCurrentEndBracket(); return itr->second.get(); } osg::ref_ptr obj = readObjectFields( className, existingObj ); _identifierMap[id] = obj; advanceToCurrentEndBracket(); return obj.release(); } osg::Object* InputStream::readObjectFields( const std::string& className, osg::Object* existingObj ) { ObjectWrapper* wrapper = Registry::instance()->getObjectWrapperManager()->findWrapper( className ); if ( !wrapper ) { OSG_WARN << "InputStream::readObject(): Unsupported wrapper class " << className << std::endl; return NULL; } _fields.push_back( className ); osg::ref_ptr obj = existingObj ? existingObj : wrapper->getProto()->cloneType(); if ( obj.valid() ) { const StringList& associates = wrapper->getAssociates(); for ( StringList::const_iterator itr=associates.begin(); itr!=associates.end(); ++itr ) { ObjectWrapper* assocWrapper = Registry::instance()->getObjectWrapperManager()->findWrapper(*itr); if ( !assocWrapper ) { OSG_WARN << "InputStream::readObject(): Unsupported associated class " << *itr << std::endl; continue; } _fields.push_back( assocWrapper->getName() ); assocWrapper->read( *this, *obj ); if ( getException() ) return NULL; _fields.pop_back(); } } _fields.pop_back(); return obj.release(); } void InputStream::readSchema( std::istream& fin ) { // Read from external ascii stream std::string line; while ( std::getline(fin, line) ) { if ( line[0]=='#' ) continue; // Comment StringList keyAndValue; split( line, keyAndValue, '=' ); if ( keyAndValue.size()<2 ) continue; setWrapperSchema( osgDB::trimEnclosingSpaces(keyAndValue[0]), osgDB::trimEnclosingSpaces(keyAndValue[1]) ); } } InputStream::ReadType InputStream::start( InputIterator* inIterator ) { _fields.clear(); _fields.push_back( "Start" ); ReadType type = READ_UNKNOWN; _in = inIterator; if ( !_in ) throwException( "InputStream: Null stream specified." ); if ( getException() ) return type; _in->setInputStream(this); // Check OSG header information unsigned int version = 0; if ( isBinary() ) { unsigned int typeValue; *this >> typeValue >> version; type = static_cast(typeValue); unsigned int attributes; *this >> attributes; if ( attributes&0x2 ) _useSchemaData = true; } if ( !isBinary() ) { std::string typeString; *this >> typeString; if ( typeString=="Scene" ) type = READ_SCENE; else if ( typeString=="Image" ) type = READ_IMAGE; else if ( typeString=="Object" ) type = READ_OBJECT; std::string osgName, osgVersion; *this >> PROPERTY("#Version") >> version; *this >> PROPERTY("#Generator") >> osgName >> osgVersion; } // Record file version for back-compatibility checking of wrappers _fileVersion = version; _fields.pop_back(); return type; } void InputStream::decompress() { if ( !isBinary() ) return; _fields.clear(); std::string compressorName; *this >> compressorName; if ( compressorName!="0" ) { std::string data; _fields.push_back( "Decompression" ); BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(compressorName); if ( !compressor ) { OSG_WARN << "InputStream::decompress(): No such compressor " << compressorName << std::endl; } if ( !compressor->decompress(*(_in->getStream()), data) ) throwException( "InputStream: Failed to decompress stream." ); if ( getException() ) return; _dataDecompress = new std::stringstream(data); _in->setStream( _dataDecompress ); _fields.pop_back(); } if ( _useSchemaData ) { _fields.push_back( "SchemaData" ); std::string schemaSource; *this >> schemaSource; std::istringstream iss( schemaSource ); readSchema( iss ); _fields.pop_back(); } } // PROTECTED METHODS void InputStream::setWrapperSchema( const std::string& name, const std::string& properties ) { ObjectWrapper* wrapper = Registry::instance()->getObjectWrapperManager()->findWrapper(name); if ( !wrapper ) { OSG_WARN << "InputStream::setSchema(): Unsupported wrapper class " << name << std::endl; return; } StringList schema, methods, keyAndValue; std::vector types; split( properties, schema ); for ( StringList::iterator itr=schema.begin(); itr!=schema.end(); ++itr ) { split( *itr, keyAndValue, ':' ); if ( keyAndValue.size()>1 ) { methods.push_back( keyAndValue.front() ); types.push_back( atoi(keyAndValue.back().c_str()) ); } else { methods.push_back( *itr ); types.push_back( 0 ); } keyAndValue.clear(); } wrapper->readSchema( methods, types ); } void InputStream::resetSchema() { 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(); wrapper->resetSchema(); } } template void InputStream::readArrayImplementation( T* a, int read_size, bool useByteSwap ) { int size = 0; *this >> size >> BEGIN_BRACKET; if ( size ) { a->resize( size ); if ( isBinary() ) { readCharArray( (char*)&((*a)[0]), read_size*size ); checkStream(); if ( useByteSwap && _byteSwap ) { for ( int i=0; i> (*a)[i]; } } *this >> END_BRACKET; }