I add some debug output (perhaps they can be merged too)
 and tests I have done seams to work.


robertosfield wrote:
> Hi Julien,
> 
> We are getting close :-)
> 
> Still passing strings as strings rather than const string&, but I can
> changed that :-)
> 
> I'm inclined to change the split function to directly add the strings
> into the associate list rather than put them in a StringList then copy
> it as this will be more efficient and require less code. Again this is
> small tweak that I can implement.
> 
> Have you tested out the feature yet?
> 
> Robert.
> 
> 
> On 2 June 2016 at 12:45, Julien Valentin <> wrote:
> 
> > Here's the new submission
> > Hope I didn't miss a thing
> > 
> > 
> > robertosfield wrote:
> > 
> > > Hi Julien,
> > > 
> > > On 2 June 2016 at 11:46, Julien Valentin <> wrote:
> > > 
> > > 
> > > > I may not be clear enough:
> > > > If you change the inheritance of a class (such Drawable inheriting from 
> > > > Node) the base class serializer is already used by other (Node 
> > > > serializer is already used in Group etc...) so you don't want to tag 
> > > > the wrapper with a version (tagging Node serializer with newversion 
> > > > would make no sense as it's already present in previous version).So You 
> > > > need finer version tagging control...
> > > > 
> > > > 
> > > 
> > > Yes, now makes perfect sense.  Given this constraint my suggestion of
> > > embedding the supported versions into the wrapper itself isn't
> > > workable.
> > > 
> > > You approach looks to be the best way forward.
> > > 
> > > Your changes aren't ready to merge as is yet as the coding style
> > > (indents/spacing/placement of {), are at odds with the rest of code
> > > around them. If we merge changes that use different coding styles the
> > > OSG code base would end up a bit of a mess and less readable and
> > > maintainable for it.  Could you adjust your code to fit in better.
> > > 
> > > Key things are:
> > > 
> > > open { on a a separate line to if etc. statement.
> > > four spaces for indentation within {} block
> > > no spaces between object->method
> > > passing objects by const& rather than as a straight objects. i.e.
> > > const std::string& rather than std::string.
> > > 
> > > There will be little ones besides these, general rule If the code
> > > looks like it's written be different developers then it's a something
> > > that grates.
> > > 
> > > Cheers,
> > > Robert.
> > > _______________________________________________
> > > osg-submissions mailing list
> > > 
> > > http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
> > > 
> > > ------------------
> > > Post generated by Mail2Forum
> > > 
> > 
> > 
> > ------------------
> > Read this topic online here:
> > http://forum.openscenegraph.org/viewtopic.php?p=67346#67346
> > 
> > 
> > 
> > 
> > _______________________________________________
> > osg-submissions mailing list
> > 
> > http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
> > 
> > 
> _______________________________________________
> osg-submissions mailing list
> 
> http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
> 
>  ------------------
> Post generated by Mail2Forum


------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=67363#67363



/* -*-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 <osg/Notify>
#include <osg/ImageSequence>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgDB/XmlParser>
#include <osgDB/FileNameUtils>
#include <osgDB/ObjectWrapper>
#include <osgDB/ConvertBase64>

using namespace osgDB;

static std::string s_lastSchema;

InputStream::InputStream( const osgDB::Options* options )
    :   _fileVersion(0), _useSchemaData(false), _forceReadingImage(false), _dataDecompress(0)
{
    BEGIN_BRACKET.set( "{", +INDENT_VALUE );
    END_BRACKET.set( "}", -INDENT_VALUE );

    if ( !options ) return;
    _options = options;

    if ( options->getPluginStringData("ForceReadingImage")=="true" )
        _forceReadingImage = true;

    if ( !options->getPluginStringData("CustomDomains").empty() )
    {
        StringList domains, keyAndValue;
        split( options->getPluginStringData("CustomDomains"), domains, ';' );
        for ( unsigned int i=0; i<domains.size(); ++i )
        {
            split( domains[i], keyAndValue, ':' );
            if ( keyAndValue.size()>1 )
                _domainVersionMap[keyAndValue.front()] = atoi(keyAndValue.back().c_str());
        }
    }

    std::string schema;
    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();
    }

    // assign dummy object to used for reading field properties that will be discarded.
    _dummyReadObject = new osg::DummyObject;
}

InputStream::~InputStream()
{
    if (_dataDecompress)
        delete _dataDecompress;
}

int InputStream::getFileVersion( const std::string& d ) const
{
    if ( d.empty() ) return _fileVersion;
    VersionMap::const_iterator itr = _domainVersionMap.find(d);
    return itr==_domainVersionMap.end() ? 0 : itr->second;
}

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::Vec2ub& v )
{
    unsigned char x, y;
    *this >> x >> y;
    v.set( x, y );
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec3ub& v )
{
    unsigned char x, y, z;
    *this >> x >> y >> z;
    v.set( x, y, z );
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec4ub& v )
{
    unsigned 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::Vec2us& v )
{
    *this >> v.x() >> v.y();
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec3us& v )
{
    *this >> v.x() >> v.y() >> v.z();
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec4us& v )
{
    *this >> v.x() >> v.y() >> v.z() >> v.w();
    return *this;
}


InputStream& InputStream::operator>>( osg::Vec2i& v )
{
    *this >> v.x() >> v.y();
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec3i& v )
{
    *this >> v.x() >> v.y() >> v.z();
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec4i& v )
{
    *this >> v.x() >> v.y() >> v.z() >> v.w();
    return *this;
}


InputStream& InputStream::operator>>( osg::Vec2ui& v )
{
    *this >> v.x() >> v.y();
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec3ui& v )
{
    *this >> v.x() >> v.y() >> v.z();
    return *this;
}

InputStream& InputStream::operator>>( osg::Vec4ui& 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<float>(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<float>(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<float>(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

InputStream& InputStream::operator>>( osg::BoundingBoxf& bb)
{
    float p0, p1, p2, p3, p4, p5;
    *this >> p0 >> p1 >> p2 >> p3>> p4>> p5;
    bb.set( p0, p1, p2, p3, p4, p5 );
    return *this;
}

InputStream& InputStream::operator>>( osg::BoundingBoxd& bb)
{
    double p0, p1, p2, p3, p4, p5;
    *this >> p0 >> p1 >> p2 >> p3>> p4>> p5;
    bb.set( p0, p1, p2, p3, p4, p5 );
    return *this;
}

InputStream& InputStream::operator>>( osg::BoundingSpheref& bs)
{
    float p0, p1, p2, p3;
    *this >> p0 >> p1 >> p2 >> p3;
    bs.set( osg::Vec3f(p0, p1, p2), p3 );
    return *this;
}

InputStream& InputStream::operator>>( osg::BoundingSphered& bs)
{
    double p0, p1, p2, p3;
    *this >> p0 >> p1 >> p2 >> p3;
    bs.set( osg::Vec3d(p0, p1, p2), p3 );
    return *this;
}



osg::ref_ptr<osg::Array> InputStream::readArray()
{
    osg::ref_ptr<osg::Array> 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, 1, CHAR_SIZE);
        array = ba;
    }
    break;
    case ID_UBYTE_ARRAY:
    {
        osg::UByteArray* uba = new osg::UByteArray;
        readArrayImplementation( uba, 1, CHAR_SIZE );
        array = uba;
    }
    break;
    case ID_SHORT_ARRAY:
    {
        osg::ShortArray* sa = new osg::ShortArray;
        readArrayImplementation( sa, 1, SHORT_SIZE );
        array = sa;
    }
    break;
    case ID_USHORT_ARRAY:
    {
        osg::UShortArray* usa = new osg::UShortArray;
        readArrayImplementation( usa, 1, SHORT_SIZE );
        array = usa;
    }
    break;
    case ID_INT_ARRAY:
    {
        osg::IntArray* ia = new osg::IntArray;
        readArrayImplementation( ia, 1, INT_SIZE );
        array = ia;
    }
    break;
    case ID_UINT_ARRAY:
    {
        osg::UIntArray* uia = new osg::UIntArray;
        readArrayImplementation( uia, 1, INT_SIZE );
        array = uia;
    }
    break;
    case ID_FLOAT_ARRAY:
    {
        osg::FloatArray* fa = new osg::FloatArray;
        readArrayImplementation( fa, 1, FLOAT_SIZE );
        array = fa;
    }
    break;
    case ID_DOUBLE_ARRAY:
    {
        osg::DoubleArray* da = new osg::DoubleArray;
        readArrayImplementation( da, 1, DOUBLE_SIZE );
        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_VEC2UB_ARRAY:
    {
        osg::Vec2ubArray* va = new osg::Vec2ubArray;
        readArrayImplementation( va, 2, CHAR_SIZE );
        array = va;
    }
    break;
    case ID_VEC3UB_ARRAY:
    {
        osg::Vec3ubArray* va = new osg::Vec3ubArray;
        readArrayImplementation( va, 3, 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_VEC2US_ARRAY:
    {
        osg::Vec2usArray* va = new osg::Vec2usArray;
        readArrayImplementation( va, 2, SHORT_SIZE );
        array = va;
    }
    break;
    case ID_VEC3US_ARRAY:
    {
        osg::Vec3usArray* va = new osg::Vec3usArray;
        readArrayImplementation( va, 3, SHORT_SIZE );
        array = va;
    }
    break;
    case ID_VEC4US_ARRAY:
    {
        osg::Vec4usArray* va = new osg::Vec4usArray;
        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;

    case ID_VEC2I_ARRAY:
    {
        osg::Vec2iArray* va = new osg::Vec2iArray;
        readArrayImplementation( va, 2, INT_SIZE );
        array = va;
    }
    break;
    case ID_VEC3I_ARRAY:
    {
        osg::Vec3iArray* va = new osg::Vec3iArray;
        readArrayImplementation( va, 3, INT_SIZE );
        array = va;
    }
    break;
    case ID_VEC4I_ARRAY:
    {
        osg::Vec4iArray* va = new osg::Vec4iArray;
        readArrayImplementation( va, 4, INT_SIZE );
        array = va;
    }
    break;

    case ID_VEC2UI_ARRAY:
    {
        osg::Vec2uiArray* va = new osg::Vec2uiArray;
        readArrayImplementation( va, 2, INT_SIZE );
        array = va;
    }
    break;
    case ID_VEC3UI_ARRAY:
    {
        osg::Vec3uiArray* va = new osg::Vec3uiArray;
        readArrayImplementation( va, 3, INT_SIZE );
        array = va;
    }
    break;
    case ID_VEC4UI_ARRAY:
    {
        osg::Vec4uiArray* va = new osg::Vec4uiArray;
        readArrayImplementation( va, 4, INT_SIZE );
        array = va;
    }
    break;

    default:
        throwException( "InputStream::readArray(): Unsupported array type." );
    }

    if ( getException() ) return NULL;
    _arrayMap[id] = array;

    return array;
}

osg::ref_ptr<osg::PrimitiveSet> InputStream::readPrimitiveSet()
{
    osg::ref_ptr<osg::PrimitiveSet> primitive = NULL;

    DEF_MAPPEE(PrimitiveType, type);
    DEF_MAPPEE(PrimitiveType, mode);
    unsigned int numInstances = 0u;
    *this >> type >> mode;
    if ( _fileVersion>96 )
    {
        *this >> numInstances;
    }

    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;
        primitive->setNumInstances( numInstances );
    }
    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<size; ++i )
        {
            *this >> value;
            dl->push_back( value );
        }
        *this >> END_BRACKET;
        primitive = dl;
        primitive->setNumInstances( numInstances );
    }
    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<size; ++i )
        {
            *this >> value;
            de->push_back( value );
        }
        *this >> END_BRACKET;
        primitive = de;
        primitive->setNumInstances( numInstances );
    }
    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<size; ++i )
        {
            *this >> value;
            de->push_back( value );
        }
        *this >> END_BRACKET;
        primitive = de;
        primitive->setNumInstances( numInstances );
    }
    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<size; ++i )
        {
            *this >> value;
            de->push_back( value );
        }
        *this >> END_BRACKET;
        primitive = de;
        primitive->setNumInstances( numInstances );
    }
    break;
    default:
        throwException( "InputStream::readPrimitiveSet(): Unsupported array type." );
    }

    if ( getException() ) return NULL;
    return primitive;
}

osg::ref_ptr<osg::Image> InputStream::readImage(bool readFromExternal)
{
    std::string className = "osg::Image";
    if ( _fileVersion>94 )  // ClassName property is only supported in 3.1.4 and higher
        *this >> PROPERTY("ClassName") >> className;

    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<osg::Image*>( 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<osg::Image> image = NULL;
    switch ( decision )
    {
    case IMAGE_INLINE_DATA:
        if ( isBinary() )
        {
            // _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 = new osg::Image;
                image->setOrigin( (osg::Image::Origin)origin );
                image->setImage( s, t, r, internalFormat, pixelFormat, dataType,
                                 (unsigned char*)data, osg::Image::USE_NEW_DELETE, packing );
            }

            // _mipmapData
            unsigned int levelSize = readSize();
            osg::Image::MipmapDataType levels(levelSize);
            for ( unsigned int i=0; i<levelSize; ++i )
            {
                *this >> levels[i];
            }
            if ( image && levelSize>0 )
                image->setMipmapLevels( levels );
            readFromExternal = false;
        }
        else     // ASCII
        {
            // _origin, _s & _t & _r, _internalTextureFormat
            int origin, s, t, r, internalFormat;
            *this >> PROPERTY("Origin") >> origin;
            *this >> PROPERTY("Size") >> s >> t >> r;
            *this >> PROPERTY("InternalTextureFormat") >> internalFormat;

            // _pixelFormat, _dataType, _packing, _allocationMode
            int pixelFormat, dataType, packing, mode;
            *this >> PROPERTY("PixelFormat") >> pixelFormat;
            *this >> PROPERTY("DataType") >> dataType;
            *this >> PROPERTY("Packing") >> packing;
            *this >> PROPERTY("AllocationMode") >> mode;

            *this >> PROPERTY("Data");
            unsigned int levelSize = readSize()-1;
            *this >> BEGIN_BRACKET;

            // _data
            std::vector<std::string> encodedData;
            encodedData.resize(levelSize+1);
            readWrappedString(encodedData.at(0));

            // Read all mipmap levels and to also add them to char* data
            // _mipmapData
            osg::Image::MipmapDataType levels(levelSize);
            for ( unsigned int i=1; i<=levelSize; ++i )
            {
                //*this >> levels[i];
                readWrappedString(encodedData.at(i));
            }

            Base64decoder d;
            char* data = d.decode(encodedData, levels);
            // remove last item as we do not need the actual size
            // of the image including all mipmaps
            levels.pop_back();

            *this >> END_BRACKET;

            if ( !data )
                throwException( "InputStream::readImage() Decoding of stream failed. Out of memory." );
            if ( getException() ) return NULL;

            image = new osg::Image;
            image->setOrigin( (osg::Image::Origin)origin );
            image->setImage( s, t, r, internalFormat, pixelFormat, dataType,
                             (unsigned char*)data, (osg::Image::AllocationMode)mode, packing );

            // Level positions (size of mipmap data)
            // from actual size of mipmap data read before
            if ( image && 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.statusMessage() << 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;
    }

    bool loadedFromCache = false;
    if ( readFromExternal && !name.empty() )
    {
        ReaderWriter::ReadResult rr = Registry::instance()->readImage(name, getOptions());
        if (rr.validImage())
        {
            image = rr.takeImage();
            loadedFromCache = rr.loadedFromCache();
        }
        else
        {
            if (!rr.success()) OSG_WARN << rr.statusMessage() << std::endl;
        }

        if ( !image && _forceReadingImage ) image = new osg::Image;
    }

    if (loadedFromCache)
    {
        // we don't want to overwrite the properties of the image in the cache as this could cause theading problems if the object is currently being used
        // so we read the properties from the file into a dummy object and discard the changes.
        osg::ref_ptr<osg::Object> temp_obj = readObjectFields("osg::Object", id, _dummyReadObject.get() );
        _identifierMap[id] = image;
    }
    else
    {
        image = readObjectFieldsOfType<osg::Image>("osg::Object", id, image.get());
        if ( image.valid() )
        {
            image->setFileName( name );
            image->setWriteHint( (osg::Image::WriteHint)writeHint );
        }
    }
    return image;
}

osg::ref_ptr<osg::Object> InputStream::readObject( osg::Object* existingObj )
{
    std::string className;
    unsigned int id = 0;
    *this >> className;

    if (className=="NULL")
    {
        return 0;
    }

    *this >> BEGIN_BRACKET >> PROPERTY("UniqueID") >> id;
    if ( getException() ) return 0;

    IdentifierMap::iterator itr = _identifierMap.find( id );
    if ( itr!=_identifierMap.end() )
    {
        advanceToCurrentEndBracket();
        return itr->second;
    }

    osg::ref_ptr<osg::Object> obj = readObjectFields( className, id, existingObj );

    advanceToCurrentEndBracket();

    return obj;
}

osg::ref_ptr<osg::Object> InputStream::readObjectFields( const std::string& className, unsigned int id, osg::Object* existingObj )
{
    ObjectWrapper* wrapper = Registry::instance()->getObjectWrapperManager()->findWrapper( className );

    if ( !wrapper )
    {
        OSG_WARN << "InputStream::readObject(): Unsupported wrapper class "
                 << className << std::endl;
        return NULL;
    }
    int inputVersion =  getFileVersion(wrapper->getDomain());
    osg::ref_ptr<osg::Object> obj = existingObj ? existingObj : wrapper->createInstance();
    _identifierMap[id] = obj;
    if ( obj.valid() )
    {
        const ObjectWrapper::RevisionAssociateList& associates = wrapper->getAssociates();
        for ( ObjectWrapper::RevisionAssociateList::const_iterator itr=associates.begin(); itr!=associates.end(); ++itr )
        {
            if ( itr->_firstVersion <= inputVersion &&
                    inputVersion <= itr->_lastVersion)
            {
                ObjectWrapper* assocWrapper = Registry::instance()->getObjectWrapperManager()->findWrapper(itr->_name);
                if ( !assocWrapper )
                {
                    OSG_WARN << "InputStream::readObject(): Unsupported associated class "
                             << itr->_name << std::endl;
                    continue;
                }
                _fields.push_back( assocWrapper->getName() );
                assocWrapper->read( *this, *obj );
                if ( getException() ) return NULL;

                _fields.pop_back();
            }
            else
            {
                OSG_WARN << "InputStream::readObject():"<<className<<" Unsupported associated class "
                         << itr->_name<<"["<<itr->_firstVersion <<","<<itr->_lastVersion <<"]for version "<<inputVersion<< std::endl;
            }
        }
    }

    return obj;
}

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<ReadType>(typeValue);

        unsigned int attributes;
        *this >> attributes;
        if ( attributes&0x4 ) inIterator->setSupportBinaryBrackets( true );
        if ( attributes&0x2 ) _useSchemaData = true;

        // Record custom domains
        if ( attributes&0x1 )
        {
            unsigned int numDomains;
            *this >> numDomains;
            for ( unsigned int i=0; i<numDomains; ++i )
            {
                std::string domainName;
                *this >> domainName;
                int domainVersion;
                *this >> domainVersion;
                _domainVersionMap[domainName] = domainVersion;
            }
        }
    }
    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;

        while ( matchString("#CustomDomain") )
        {
            std::string domainName;
            *this >> domainName;
            int domainVersion;
            *this >> domainVersion;
            _domainVersionMap[domainName] = domainVersion;
        }
    }

    // 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;
    ObjectWrapper::TypeList 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( static_cast<BaseSerializer::Type>(atoi(keyAndValue.back().c_str())) );
        }
        else
        {
            methods.push_back( *itr );
            types.push_back( BaseSerializer::RW_UNDEFINED );
        }
        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<typename T>
void InputStream::readArrayImplementation( T* a, unsigned int numComponentsPerElements, unsigned int componentSizeInBytes )
{
    int size = 0;
    *this >> size >> BEGIN_BRACKET;
    if ( size )
    {
        a->resize( size );
        if ( isBinary() )
        {
            readComponentArray( (char*)&((*a)[0]), size, numComponentsPerElements, componentSizeInBytes );
            checkStream();
        }
        else
        {
            for ( int i=0; i<size; ++i )
                *this >> (*a)[i];
        }
    }
    *this >> END_BRACKET;
}
/* -*-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 <osg/Version>
#include <osg/Notify>
#include <osgDB/ConvertBase64>
#include <osgDB/FileUtils>
#include <osgDB/WriteFile>
#include <osgDB/ObjectWrapper>
#include <fstream>
#include <sstream>
#include <stdlib.h>

using namespace osgDB;

OutputStream::OutputStream( const osgDB::Options* options )
    :   _writeImageHint(WRITE_USE_IMAGE_HINT), _useSchemaData(false), _useRobustBinaryFormat(true)
{
    BEGIN_BRACKET.set( "{", +INDENT_VALUE );
    END_BRACKET.set( "}", -INDENT_VALUE );

    if ( !options ) return;
    _options = options;

    if ( options->getPluginStringData("RobustBinaryFormat")=="false" )
        _useRobustBinaryFormat = false;
    if ( options->getPluginStringData("SchemaData")=="true" )
        _useSchemaData = true;
    if ( !options->getPluginStringData("SchemaFile").empty() )
        _schemaName = options->getPluginStringData("SchemaFile");
    if ( !options->getPluginStringData("Compressor").empty() )
        _compressorName = options->getPluginStringData("Compressor");
    if ( !options->getPluginStringData("WriteImageHint").empty() )
    {
        std::string hintString = options->getPluginStringData("WriteImageHint");
        if ( hintString=="IncludeData" ) _writeImageHint = WRITE_INLINE_DATA;
        else if ( hintString=="IncludeFile" ) _writeImageHint = WRITE_INLINE_FILE;
        else if ( hintString=="UseExternal" ) _writeImageHint = WRITE_USE_EXTERNAL;
        else if ( hintString=="WriteOut" ) _writeImageHint = WRITE_EXTERNAL_FILE;
    }

    if ( !options->getPluginStringData("CustomDomains").empty() )
    {
        StringList domains, keyAndValue;
        split( options->getPluginStringData("CustomDomains"), domains, ';' );
        for ( unsigned int i=0; i<domains.size(); ++i )
        {
            split( domains[i], keyAndValue, ':' );
            if ( keyAndValue.size()>1 )
                _domainVersionMap[keyAndValue.front()] = atoi(keyAndValue.back().c_str());
        }
    }
}

OutputStream::~OutputStream()
{
}

int OutputStream::getFileVersion( const std::string& d ) const
{
    if ( d.empty() ) return OPENSCENEGRAPH_SOVERSION;
    VersionMap::const_iterator itr = _domainVersionMap.find(d);
    return itr==_domainVersionMap.end() ? 0 : itr->second;
}

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::Vec2ub& v )
{
    *this << v.x() << v.y();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec3ub& v )
{
    *this << v.x() << v.y() << v.z();
    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::Vec2us& v )
{
    *this << v.x() << v.y();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec3us& v )
{
    *this << v.x() << v.y() << v.z();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec4us& 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::Vec2i& v )
{
    *this << v.x() << v.y();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec3i& v )
{
    *this << v.x() << v.y() << v.z();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec4i& v )
{
    *this << v.x() << v.y() << v.z() << v.w();
    return *this;
}


OutputStream& OutputStream::operator<<( const osg::Vec2ui& v )
{
    *this << v.x() << v.y();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec3ui& v )
{
    *this << v.x() << v.y() << v.z();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Vec4ui& 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::BoundingBoxf& bb)
{
    *this << bb.xMin() << bb.yMin() << bb.zMin() << bb.xMax() << bb.yMax() << bb.zMax();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::BoundingBoxd& bb)
{
    *this << bb.xMin() << bb.yMin() << bb.zMin() << bb.xMax() << bb.yMax() << bb.zMax();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::BoundingSpheref& bs)
{
    *this << bs.center().x() << bs.center().y() << bs.center().z() << bs.radius();
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::BoundingSphered& bs)
{
    *this << bs.center().x() << bs.center().y() << bs.center().z() << bs.radius();
    return *this;
}

#if 0
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;
}
#else
OutputStream& OutputStream::operator<<( const osg::Matrixf& mat )
{
    *this << BEGIN_BRACKET << std::endl;
    for ( int r=0; r<4; ++r )
    {
        *this << (double)mat(r, 0) << (double)mat(r, 1)
              << (double)mat(r, 2) << (double)mat(r, 3) << std::endl;
    }
    *this << END_BRACKET << std::endl;
    return *this;
}

OutputStream& OutputStream::operator<<( const osg::Matrixd& mat )
{
    *this << 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;
}
#endif

void OutputStream::writeArray( const osg::Array* a )
{
    if ( !a ) return;

    bool newID = false;
    unsigned int id = findOrCreateArrayID( a, newID );
    *this << PROPERTY("ArrayID") << id;
    if ( !newID )  // Shared array
    {
        *this << std::endl;
        return;
    }

    switch ( a->getType() )
    {
    case osg::Array::ByteArrayType:
        *this << MAPPEE(ArrayType, ID_BYTE_ARRAY);
        writeArrayImplementation( static_cast<const osg::ByteArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::UByteArrayType:
        *this << MAPPEE(ArrayType, ID_UBYTE_ARRAY);
        writeArrayImplementation( static_cast<const osg::UByteArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::ShortArrayType:
        *this << MAPPEE(ArrayType, ID_SHORT_ARRAY);
        writeArrayImplementation( static_cast<const osg::ShortArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::UShortArrayType:
        *this << MAPPEE(ArrayType, ID_USHORT_ARRAY);
        writeArrayImplementation( static_cast<const osg::UShortArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::IntArrayType:
        *this << MAPPEE(ArrayType, ID_INT_ARRAY);
        writeArrayImplementation( static_cast<const osg::IntArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::UIntArrayType:
        *this << MAPPEE(ArrayType, ID_UINT_ARRAY);
        writeArrayImplementation( static_cast<const osg::UIntArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::FloatArrayType:
        *this << MAPPEE(ArrayType, ID_FLOAT_ARRAY);
        writeArrayImplementation( static_cast<const osg::FloatArray*>(a), a->getNumElements(), 4 );
        break;
    case osg::Array::DoubleArrayType:
        *this << MAPPEE(ArrayType, ID_DOUBLE_ARRAY);
        writeArrayImplementation( static_cast<const osg::DoubleArray*>(a), a->getNumElements(), 4 );
        break;

    case osg::Array::Vec2bArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2B_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2bArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3bArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3B_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3bArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4bArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4B_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4bArray*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2ubArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2UB_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2ubArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3ubArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3UB_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3ubArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4ubArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4UB_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4ubArray*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2sArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2S_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2sArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3sArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3S_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3sArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4sArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4S_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4sArray*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2usArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2US_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2usArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3usArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3US_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3usArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4usArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4US_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4usArray*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2ArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2Array*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3ArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3Array*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4ArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4Array*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2dArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2D_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2dArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3dArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3D_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3dArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4dArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4D_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4dArray*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2iArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2I_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2iArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3iArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3I_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3iArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4iArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4I_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4iArray*>(a), a->getNumElements() );
        break;

    case osg::Array::Vec2uiArrayType:
        *this << MAPPEE(ArrayType, ID_VEC2UI_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec2uiArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec3uiArrayType:
        *this << MAPPEE(ArrayType, ID_VEC3UI_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec3uiArray*>(a), a->getNumElements() );
        break;
    case osg::Array::Vec4uiArrayType:
        *this << MAPPEE(ArrayType, ID_VEC4UI_ARRAY);
        writeArrayImplementation( static_cast<const osg::Vec4uiArray*>(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<const osg::DrawArrays*>(p);
            *this << MAPPEE(PrimitiveType, da->getMode()) << da->getNumInstances()
                  << da->getFirst() << da->getCount() << std::endl;
        }
        break;
    case osg::PrimitiveSet::DrawArrayLengthsPrimitiveType:
        *this << MAPPEE(PrimitiveType, ID_DRAWARRAY_LENGTH);
        {
            const osg::DrawArrayLengths* dl = static_cast<const osg::DrawArrayLengths*>(p);
            *this << MAPPEE(PrimitiveType, dl->getMode()) << dl->getNumInstances() << dl->getFirst();
            writeArrayImplementation( dl, dl->size(), 4 );
        }
        break;
    case osg::PrimitiveSet::DrawElementsUBytePrimitiveType:
        *this << MAPPEE(PrimitiveType, ID_DRAWELEMENTS_UBYTE);
        {
            const osg::DrawElementsUByte* de = static_cast<const osg::DrawElementsUByte*>(p);
            *this << MAPPEE(PrimitiveType, de->getMode()) << de->getNumInstances();
            writeArrayImplementation( de, de->size(), 4 );
        }
        break;
    case osg::PrimitiveSet::DrawElementsUShortPrimitiveType:
        *this << MAPPEE(PrimitiveType, ID_DRAWELEMENTS_USHORT);
        {
            const osg::DrawElementsUShort* de = static_cast<const osg::DrawElementsUShort*>(p);
            *this << MAPPEE(PrimitiveType, de->getMode()) << de->getNumInstances();
            writeArrayImplementation( de, de->size(), 4 );
        }
        break;
    case osg::PrimitiveSet::DrawElementsUIntPrimitiveType:
        *this << MAPPEE(PrimitiveType, ID_DRAWELEMENTS_UINT);
        {
            const osg::DrawElementsUInt* de = static_cast<const osg::DrawElementsUInt*>(p);
            *this << MAPPEE(PrimitiveType, de->getMode()) << de->getNumInstances();
            writeArrayImplementation( de, de->size(), 4 );
        }
        break;
    default:
        throwException( "OutputStream::writePrimitiveSet(): Unsupported primitive type." );
    }
}

void OutputStream::writeImage( const osg::Image* img )
{
    if ( !img ) return;

    std::string name = img->libraryName();
    name += std::string("::") + img->className();

    bool newID = false;
    unsigned int id = findOrCreateObjectID( img, newID );

    *this << PROPERTY("ClassName") << name << std::endl;   // Write object name
    *this << PROPERTY("UniqueID") << id << std::endl;      // Write image ID
    if ( getException() ) return;

    if (newID)
    {
        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_WRITE_OUT;
            break;
        case OutputStream::WRITE_USE_EXTERNAL:
            decision = IMAGE_EXTERNAL;
            break;
        default:
            if ( img->getWriteHint()==osg::Image::EXTERNAL_FILE )
                decision = IMAGE_EXTERNAL;
            else if ( isBinary() )
                decision = IMAGE_INLINE_DATA;
            break;
        }


        std::string imageFileName = img->getFileName();
        if ( decision==IMAGE_WRITE_OUT || _writeImageHint==WRITE_EXTERNAL_FILE )
        {
            if (imageFileName.empty())
            {
                OSG_NOTICE<<"Empty Image::FileName resetting to image.dds"<<std::endl;
                imageFileName = "image.dds";
            }

            bool result = osgDB::writeImageFile( *img, imageFileName );
            OSG_NOTICE << "OutputStream::writeImage(): Write image data to external file " << imageFileName << std::endl;
            if ( !result )
            {
                OSG_WARN << "OutputStream::writeImage(): Failed to write " << img->getFileName() << std::endl;
            }
        }

        *this << PROPERTY("FileName");
        writeWrappedString(imageFileName);
        *this << std::endl;
        *this << PROPERTY("WriteHint") << (int)img->getWriteHint();
        if ( getException() ) return;

        *this << decision << 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
                writeSize( static_cast<unsigned int>(img->getTotalSizeInBytesIncludingMipmaps()) );

                for(osg::Image::DataIterator img_itr(img); img_itr.valid(); ++img_itr)
                {
                    writeCharArray( (char*)img_itr.data(), img_itr.size() );
                }

                // _mipmapData
                unsigned int numMipmaps = img->getNumMipmapLevels()-1;
                writeSize(numMipmaps);
                int s = img->s();
                int t = img->t();
                int r = img->r();
                unsigned int offset = 0;
                for (unsigned int i=0; i<numMipmaps; ++i)
                {
                    unsigned int size = osg::Image::computeImageSizeInBytes(s,t,r,img->getPixelFormat(),img->getDataType(),img->getPacking());
                    offset += size;

                    *this << offset;

                    s >>= 1;
                    t >>= 1;
                    r >>= 1;
                    if (s<1) s=1;
                    if (t<1) t=1;
                    if (r<1) r=1;
                }
            }
            else     // ASCII
            {
                *this << PROPERTY("Origin") << img->getOrigin() << std::endl;  // _origin
                *this << PROPERTY("Size") << img->s() << img->t() << img->r() << std::endl; // _s & _t & _r
                *this << PROPERTY("InternalTextureFormat") << img->getInternalTextureFormat() << std::endl;  // _internalTextureFormat
                *this << PROPERTY("PixelFormat") << img->getPixelFormat() << std::endl;  // _pixelFormat
                *this << PROPERTY("DataType") << img->getDataType() << std::endl;  // _dataType
                *this << PROPERTY("Packing") << img->getPacking() << std::endl;  // _packing
                *this << PROPERTY("AllocationMode") << img->getAllocationMode() << std::endl;  // _allocationMode

                // _data
                *this << PROPERTY("Data") << img->getNumMipmapLevels();
                *this << BEGIN_BRACKET << std::endl;

                Base64encoder e;
                for(osg::Image::DataIterator img_itr(img); img_itr.valid(); ++img_itr)
                {
                    std::string encodedData;
                    e.encode((char*)img_itr.data(), img_itr.size(), encodedData);
                    // Each set of data is written into a separate string so we can
                    // distiguish between main data and all mipmap levels, so writing
                    // mipmap size is not required for ASCII mode.
                    writeWrappedString(encodedData);
                }

                *this << END_BRACKET << std::endl;
            }
            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_WARN << "OutputStream::writeImage(): Failed to open image file "
                             << img->getFileName() << std::endl;
                    *this << (unsigned int)0;
                }
            }
            break;
        case IMAGE_EXTERNAL:
            break;
        default:
            break;
        }

        writeObjectFields( img, "osg::Object" );
    }

    // *this << END_BRACKET << std::endl;
}

void OutputStream::writeObject( const osg::Object* obj )
{
    if ( !obj )
    {
        *this << std::string("NULL") << std::endl;  // Write NULL token.
        return;
    }

    std::string name = obj->libraryName();
    name += std::string("::") + obj->className();

    bool newID = false;
    unsigned int id = findOrCreateObjectID( obj, newID );

    *this << name << BEGIN_BRACKET << std::endl;       // Write object name
    *this << PROPERTY("UniqueID") << id << std::endl;  // Write object ID
    if ( getException() ) return;

    if (newID)
    {
        writeObjectFields(obj);
    }

    *this << END_BRACKET << std::endl;
}

void OutputStream::writeObjectFields( const osg::Object* obj )
{
    std::string name = obj->libraryName();
    name += std::string("::") + obj->className();
    writeObjectFields(obj, name);
}

void OutputStream::writeObjectFields( const osg::Object* obj, const std::string& name )
{
    // OSG_NOTICE<<"OutputStream::writeObjectFields("<<obj->className()<<", name="<<name<<")"<<std::endl;

    ObjectWrapper* wrapper = Registry::instance()->getObjectWrapperManager()->findWrapper( name );

    if ( !wrapper )
    {
        OSG_WARN << "OutputStream::writeObject(): Unsupported wrapper class "
                 << name << std::endl;
        return;
    }
    int outputVersion =  getFileVersion(wrapper->getDomain());
    ObjectWrapper::RevisionAssociateList associates = wrapper->getAssociates();
    ///

    for ( ObjectWrapper::RevisionAssociateList::const_iterator itr=associates.begin(); itr!=associates.end(); ++itr )
    {
        if ( itr->_firstVersion <= outputVersion &&
                outputVersion <= itr->_lastVersion)
        {
            const std::string& assocName = itr->_name;
            ObjectWrapper* assocWrapper = Registry::instance()->getObjectWrapperManager()->findWrapper(assocName);
            if ( !assocWrapper )
            {
                OSG_WARN << "OutputStream::writeObject(): Unsupported associated class "
                         << assocName << std::endl;
                continue;
            }
            else if ( _useSchemaData )
            {
                if ( _inbuiltSchemaMap.find(assocName)==_inbuiltSchemaMap.end() )
                {
                    StringList properties;
                    ObjectWrapper::TypeList types;
                    assocWrapper->writeSchema( properties, types );

                    unsigned int size = osg::minimum( properties.size(), types.size() );
                    if ( size>0 )
                    {
                        std::stringstream propertiesStream;
                        for ( unsigned int i=0; i<size; ++i )
                        {
                            propertiesStream << properties[i] << ":" << types[i] << " ";
                        }
                        _inbuiltSchemaMap[assocName] = propertiesStream.str();
                    }
                }
            }

            _fields.push_back( assocWrapper->getName() );

            assocWrapper->write( *this, *obj );
            if ( getException() ) return;

            _fields.pop_back();
        }
        else
            OSG_WARN << "OutputStream::writeObject():"<<name<<" Unsupported associated class "
                     << itr->_name<<"["<<itr->_firstVersion <<","<<itr->_lastVersion <<"]for output version "<<outputVersion<< 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)OPENSCENEGRAPH_SOVERSION;

        bool useCompressSource = false;
        unsigned int attributes = 0;

        // From SOVERSION 98, start to support custom wrapper domains, enabling the attribute bit
        if ( _domainVersionMap.size()>0 ) attributes |= 0x1;

        if ( _useSchemaData )
        {
            attributes |= 0x2;  // Record if we use inbuilt schema data or not
            useCompressSource = true;
        }

        // From SOVERSION 98, start to support binary begin/end brackets so we can easily ignore
        // errors and unsupport classes, enabling the attribute bit
        if ( _useRobustBinaryFormat )
        {
            outIterator->setSupportBinaryBrackets( true );
            attributes |= 0x4;
        }
        *this << attributes;

        // Record all custom versions
        if ( _domainVersionMap.size()>0 )
        {
            unsigned int numDomains = _domainVersionMap.size();
            *this << numDomains;
            for ( VersionMap::iterator itr=_domainVersionMap.begin();
                    itr!=_domainVersionMap.end(); ++itr )
            {
                *this << itr->first << itr->second;
            }
        }

        if ( !_compressorName.empty() )
        {
            BaseCompressor* compressor = Registry::instance()->getObjectWrapperManager()->findCompressor(_compressorName);
            if ( !compressor )
            {
                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)OPENSCENEGRAPH_SOVERSION << std::endl;
        *this << PROPERTY("#Generator") << std::string("OpenSceneGraph")
              << std::string(osgGetVersion()) << std::endl;
        if ( _domainVersionMap.size()>0 )
        {
            for ( VersionMap::iterator itr=_domainVersionMap.begin();
                    itr!=_domainVersionMap.end(); ++itr )
            {
                *this << PROPERTY("#CustomDomain") << itr->first << itr->second << 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 ( SchemaMap::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;
        ObjectWrapper::TypeList types;
        wrapper->writeSchema( properties, types );

        std::string propertiesString;
        unsigned int size = osg::minimum( properties.size(), types.size() );
        for ( unsigned int i=0; i<size; ++i )
        {
            fout << " " << properties[i] << ":" << types[i];
        }
        fout << std::endl;
    }
}

// PROTECTED METHODS

template<typename T>
void OutputStream::writeArrayImplementation( const T* a, int write_size, unsigned int numInRow )
{
    *this << write_size << BEGIN_BRACKET;
    if ( numInRow>1 )
    {
        for ( int i=0; i<write_size; ++i )
        {
            if ( !(i%numInRow) )
            {
                *this << std::endl << (*a)[i];
            }
            else
                *this << (*a)[i];
        }
        *this << std::endl;
    }
    else
    {
        *this << std::endl;
        for ( int i=0; i<write_size; ++i )
            *this << (*a)[i] << std::endl;
    }
    *this << END_BRACKET << std::endl;
}

unsigned int OutputStream::findOrCreateArrayID( const osg::Array* array, bool& newID )
{
    ArrayMap::iterator itr = _arrayMap.find( array );
    if ( itr==_arrayMap.end() )
    {
        unsigned int id = _arrayMap.size()+1;
        _arrayMap[array] = id;
        newID = true;
        return id;
    }
    newID = false;
    return itr->second;
}

unsigned int OutputStream::findOrCreateObjectID( const osg::Object* obj, bool& newID )
{
    ObjectMap::iterator itr = _objectMap.find( obj );
    if ( itr==_objectMap.end() )
    {
        unsigned int id = _objectMap.size()+1;
        _objectMap[obj] = id;
        newID = true;
        return id;
    }
    newID = false;
    return itr->second;
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to