- Main Page
- Related Pages
- Modules
- Classes
- Examples

# XmlGeometryCache.cpp

    //-
    // ==========================================================================
    // Copyright 2009 Autodesk, Inc. All rights reserved.
    //
    // Use of this software is subject to the terms of the Autodesk
    // license agreement provided at the time of installation or download,
    // or which otherwise accompanies this software in either electronic
    // or hard copy form.
    // ==========================================================================
    //+

    //
    // Description:
    //  This plug-in provides an example of the use of MPxCacheFormat.
    //
    //  In this example, the cache files are written in xml format.
    //

    #include <string>
    #include <stack>
    #include <fstream>
    #include <sstream>

    #include <assert.h>
    #include <stdlib.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MGlobal.h>
    #include <maya/MString.h>
    #include <maya/MPxCacheFormat.h>
    #include <maya/MTime.h>
    #include <maya/MFloatVectorArray.h>
    #include <maya/MDoubleArray.h>
    #include <maya/MFloatArray.h>
    #include <maya/MIntArray.h>
    #include <maya/MVectorArray.h>

    using namespace std;

    class XmlCacheFormat : public MPxCacheFormat
    {
    public:
        XmlCacheFormat();
        ~XmlCacheFormat();

        static void*    creator();
        static void     setPluginName( const MString& name );
        static MString  translatorName();

        MStatus         isValid();

        MStatus         open( const MString& fileName, FileAccessMode mode );
        void            close();

        MStatus         readHeader();
        MStatus         writeHeader( const MString& version, MTime& startTime, MTime& endTime );

        void            beginWriteChunk();
        void            endWriteChunk();
        MStatus         beginReadChunk();
        void            endReadChunk();

        MStatus         writeTime( MTime& time );
        MStatus         readTime( MTime& time );
        MStatus         findTime( MTime& time, MTime& foundTime );
        MStatus         readNextTime( MTime& foundTime );

        unsigned        readArraySize();

        MStatus         writeDoubleArray( const MDoubleArray& );
        MStatus         readDoubleArray( MDoubleArray&, unsigned size );
        MStatus         writeFloatArray( const MFloatArray& );
        MStatus         readFloatArray( MFloatArray&, unsigned size );
        MStatus         writeDoubleVectorArray( const MVectorArray& array );
        MStatus         readDoubleVectorArray( MVectorArray&, unsigned arraySize );
        MStatus         writeFloatVectorArray( const MFloatVectorArray& array );
        MStatus         readFloatVectorArray( MFloatVectorArray& array, unsigned arraySize );

        MStatus         writeChannelName( const MString& name );
        MStatus         findChannelName( const MString& name );
        MStatus         readChannelName( MString& name );

        MStatus         writeInt32( int );
        int             readInt32();
        MStatus         rewind();

        MString         extension();

    protected:

        static MString  comment( const MString& text );
        static MString  quote( const MString& text );

        static MString  fExtension;
        static MString  fCacheFormatName;

    private:
        void            startXmlBlock( string& t );
        void            endXmlBlock();
        void            writeXmlTagValue( string& tag, string value );
        void            writeXmlTagValue( string& tag, int value );
        bool            readXmlTagValue( string tag, MStringArray& value );
        bool            readXmlTagValueInChunk( string tag, MStringArray& values );
        void            readXmlTag( string& value );
        bool            findXmlStartTag( string& tag );
        bool            findXmlStartTagInChunk( string& tag );
        bool            findXmlEndTag( string& tag );
        void            writeXmlValue( string& value );
        void            writeXmlValue( double value );
        void            writeXmlValue( float value );
        void            writeXmlValue( int value );

        MString         fFileName;
        fstream         fFile;
        stack<string>   fXmlStack;
        FileAccessMode  fMode;

    };

    MString XmlCacheFormat::fExtension = "mc";          // For files on disk
    MString XmlCacheFormat::fCacheFormatName = "xml";   // For presentation in GUI

    inline MString XmlCacheFormat::translatorName()
    {
        return fCacheFormatName;
    }

    void* XmlCacheFormat::creator()
    {
        return new XmlCacheFormat();
    }

    // ****************************************
    //
    //  Helper functions

    string XMLSTARTTAG( string x )
    {
        return ( "<" + x + ">" );
    }

    string XMLENDTAG( string x )
    {
        return ( "</" + x + ">" );
    }

    string cacheTag("awGeoCache");
    string startTimeTag("startTime");
    string endTimeTag("endTime");
    string versionTag("version");
    string timeTag("time");
    string sizeTag("size");
    string intTag("integer32");
    string doubleArrayTag("doubleArray");
    string floatArrayTag("floatArray");
    string doubleVectorArrayTag("doubleVectorArray");
    string floatVectorArrayTag("floatVectorArray");
    string channelTag("channel");
    string chunkTag("chunk");

    XmlCacheFormat::XmlCacheFormat()
    {
    }

    XmlCacheFormat::~XmlCacheFormat()
    {
        close();
    }

    MStatus
    XmlCacheFormat::open( const MString& fileName, FileAccessMode mode )
    {
        bool rtn = true;

        assert((fileName.length() > 0));

        fFileName = fileName;

        if( mode == kWrite ) {
            fFile.open(fFileName.asChar(), ios::out);
        }
        else if (mode == kReadWrite) {
            fFile.open(fFileName.asChar(), ios::app);
        } else {
            fFile.open(fFileName.asChar(), ios::in);
        }

        if (!fFile.is_open()) {
            rtn = false;
        } else {
            if (mode == kRead) {
                rtn = readHeader();
            }
        }

        return rtn ? MS::kSuccess : MS::kFailure ;
    }

    MStatus
    XmlCacheFormat::isValid()
    {
        bool rtn = fFile.is_open();
        return rtn ? MS::kSuccess : MS::kFailure ;
    }

    MStatus
    XmlCacheFormat::readHeader()
    {
        bool rtn = false;
        if (kWrite != fMode) {
            if( fFile.is_open() ) {
                string tag;
                readXmlTag( tag );

                if( tag == XMLSTARTTAG(cacheTag) )
                {
                    MStringArray value;
                    readXmlTagValue( versionTag, value );

                    readXmlTagValue( startTimeTag, value );

                    readXmlTagValue( endTimeTag, value );

                    readXmlTag( tag );  // Should be header close tag, check
                    if( tag != XMLENDTAG(cacheTag) )
                    {
                        // TODO - handle this error case
                    }

                    rtn = true;
                }
            }
        }

        return rtn ? MS::kSuccess : MS::kFailure ;
    }

    MStatus
    XmlCacheFormat::rewind()
    {
        if( fFile.is_open() )
        {
            close();
                if(!open(fFileName, kRead ))
            {
                return MS::kFailure;
            }
            return MS::kSuccess;
        }
        else
        {
            return MS::kFailure;
        }
    }

    void XmlCacheFormat::close()
    {
        if( fFile.is_open() ) {
        fFile.close();
        }
    }

    MStatus XmlCacheFormat::writeInt32( int i )
    {
            writeXmlTagValue(intTag, i);
        return MS::kSuccess;
    }

    int
    XmlCacheFormat::readInt32()
    {
        MStringArray value;
        readXmlTagValue(intTag, value);
        return atoi( value[0].asChar() );
    }

    MStatus
    XmlCacheFormat::writeHeader( const MString& version, MTime& startTime, MTime& endTime )
    {
        stringstream oss;

        startXmlBlock(cacheTag);
        string v(version.asChar());
        writeXmlTagValue(versionTag, v);

        oss << startTime;
        writeXmlTagValue(startTimeTag, oss.str() );

        oss.str("");
        oss.clear();
        oss << endTime;
        writeXmlTagValue(endTimeTag, oss.str() );

        endXmlBlock();
        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::readTime( MTime& time )
    {
        MStringArray timeValue;
        readXmlTagValue(timeTag, timeValue);
        time.setValue( strtod( timeValue[0].asChar(), NULL ) );

        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::writeTime( MTime& time )
    {
        stringstream oss;
        oss << time;
        writeXmlTagValue(timeTag, oss.str() );
        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::findChannelName( const MString& name )
    //
    //  Given that the right time has already been found, find the name
    //  of the channel we're trying to read.
    //
    {
        MStringArray value;
        while (readXmlTagValueInChunk(channelTag, value))
        {
            if( value.length() == 1 && value[0] == name )
            {
                return MS::kSuccess;
            }
        }

        return MS::kFailure;
    }

    MStatus
    XmlCacheFormat::readChannelName( MString& name )
    //
    //  Given that the right time has already been found, find the name
    //  of the channel we're trying to read.
    //
    //  If no more channels exist, return false. Some callers rely on this false return
    //  value to terminate scanning for channels, thus it's not an error condition.
    //
    //  TODO: the current implementation assumes there are numChannels of data stored
    //  for every time.  This assumption needs to be removed.
    //
    {
        MStringArray value;
        readXmlTagValueInChunk(channelTag, value);

        name = value[0];

        return name.length() == 0 ? MS::kFailure : MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::readNextTime( MTime& foundTime )
    //
    // Read the next time based on the current read position.
    //
    {
        MTime readAwTime(0.0, MTime::k6000FPS);
        bool ret = readTime(readAwTime);
        foundTime = readAwTime;

        return ret ? MS::kSuccess : MS::kFailure ;
    }

    MStatus
    XmlCacheFormat::findTime( MTime& time, MTime& foundTime )
    //
    // Find the biggest cached time, which is smaller or equal to
    // seekTime and return foundTime
    //
    {
        MTime timeTolerance(0.0, MTime::k6000FPS);
        MTime seekTime(time);
        MTime preTime( seekTime - timeTolerance );
        MTime postTime( seekTime + timeTolerance );

        bool fileRewound = false;
        while (1)
        {
            bool timeTagFound = beginReadChunk();
            if ( ! timeTagFound && !fileRewound )
            {
                if(!rewind())
                {
                    return MS::kFailure;
                }
                fileRewound = true;
                timeTagFound = beginReadChunk();
            }
            if ( timeTagFound )
            {
                MTime rTime(0.0, MTime::k6000FPS);
                readTime(rTime);

                if(rTime >= preTime && rTime <= postTime)
                {
                    foundTime = rTime;
                    return MS::kSuccess;
                }
                if(rTime > postTime )
                {
                    if (!fileRewound)
                    {
                        if(!rewind())
                        {
                            return MS::kFailure;
                        }
                        fileRewound = true;
                    }
                    else
                    {
                        // Time could not be found
                        //
                        return MS::kFailure;
                    }
                }
                else
                {
                    fileRewound = true;
                }
                endReadChunk();
            }
            else
            {
                // Not a valid disk cache file.
                break;
            }
        }

        return MS::kFailure;
    }

    MStatus
    XmlCacheFormat::writeChannelName( const MString& name )
    {
        string chan = name.asChar();
        writeXmlTagValue( channelTag, chan );
        return MS::kSuccess;
    }

    void XmlCacheFormat::beginWriteChunk()
    {
        startXmlBlock(chunkTag);
    }

    void XmlCacheFormat::endWriteChunk()
    {
        endXmlBlock();
    }

    MStatus XmlCacheFormat::beginReadChunk()
    {
        return findXmlStartTag(chunkTag) ? MS::kSuccess : MS::kFailure;
    }

    void XmlCacheFormat::endReadChunk()
    {
        findXmlEndTag(chunkTag);
    }

    MStatus
    XmlCacheFormat::writeDoubleArray( const MDoubleArray& array )
    {
        int size = array.length();
        assert(size != 0);

        writeXmlTagValue(sizeTag,size);

        startXmlBlock( doubleArrayTag );
        for(int i = 0; i < size; i++)
        {
            writeXmlValue(array[i]);
        }
        endXmlBlock();
        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::writeFloatArray( const MFloatArray& array )
    {
        int size = array.length();
        assert(size != 0);

        writeXmlTagValue(sizeTag,size);

        startXmlBlock( floatArrayTag );
        for(int i = 0; i < size; i++)
        {
            writeXmlValue(array[i]);
        }
        endXmlBlock();
        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::writeDoubleVectorArray( const MVectorArray& array )
    {
        int size = array.length();
        assert(size != 0);

        writeXmlTagValue(sizeTag,size);

        startXmlBlock( doubleVectorArrayTag );
        for(int i = 0; i < size; i++)
        {
            writeXmlValue(array[i][0]);
            writeXmlValue(array[i][1]);
            writeXmlValue(array[i][2]);
            string endl("\n");
            writeXmlValue(endl);
        }
        endXmlBlock();
        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::writeFloatVectorArray( const MFloatVectorArray& array )
    {
        int size = array.length();
        assert(size != 0);

        writeXmlTagValue(sizeTag,size);

        startXmlBlock( floatVectorArrayTag );
        for(int i = 0; i < size; i++)
        {
            writeXmlValue(array[i][0]);
            writeXmlValue(array[i][1]);
            writeXmlValue(array[i][2]);
            string endl("\n");
            writeXmlValue(endl);
        }
        endXmlBlock();
        return MS::kSuccess;
    }

    unsigned
    XmlCacheFormat::readArraySize()
    {
        MStringArray value;

        if (readXmlTagValue(sizeTag, value))
        {
            unsigned readSize = (unsigned) atoi( value[0].asChar() );
            return readSize;
        }

        return 0;
    }

    MStatus
    XmlCacheFormat::readDoubleArray( MDoubleArray& array, unsigned arraySize )
    {
        MStringArray value;
        readXmlTagValue(doubleArrayTag, value);

        assert( value.length() == arraySize );
        array.setLength( arraySize );
        for ( unsigned int i = 0; i < value.length(); i++ )
        {
            array[i] = strtod( value[i].asChar(), NULL );
        }

        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::readFloatArray( MFloatArray& array, unsigned arraySize )
    {
        MStringArray value;
        readXmlTagValue(floatArrayTag, value);

        assert( value.length() == arraySize );
        array.setLength( arraySize );
        for ( unsigned int i = 0; i < value.length(); i++ )
        {
            array[i] = (float)strtod( value[i].asChar(), NULL );
        }

        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::readDoubleVectorArray( MVectorArray& array, unsigned arraySize )
    {
        MStringArray value;
        if( !readXmlTagValue(doubleVectorArrayTag, value) )
        {
            return MS::kFailure;
        }

        assert( value.length() == arraySize * 3 );
        array.setLength( arraySize );
        for (unsigned i = 0; i < arraySize; i++ )
        {
            double v[3];
            v[0] = strtod( value[i*3].asChar(), NULL );
            v[1] = strtod( value[i*3+1].asChar(), NULL );
            v[2] = strtod( value[i*3+2].asChar(), NULL );

            array.set( v, i );
        }

        return MS::kSuccess;
    }

    MStatus
    XmlCacheFormat::readFloatVectorArray( MFloatVectorArray& array, unsigned arraySize )
    {
        MStringArray value;
        readXmlTagValue(floatVectorArrayTag, value);

        assert( value.length() == arraySize * 3 );

        array.clear();
        array.setLength( arraySize );
        for (unsigned i = 0; i < arraySize; i++ )
        {
            float v[3];
            v[0] = (float)strtod( value[i*3].asChar(), NULL );
            v[1] = (float)strtod( value[i*3+1].asChar(), NULL );
            v[2] = (float)strtod( value[i*3+2].asChar(), NULL );

            array.set(v,i);
        }

        return MS::kSuccess;
    }

    MString
    XmlCacheFormat::extension()
    {
        return fExtension;
    }

    // ****************************************
    //
    //  Helper functions
    //

    void XmlCacheFormat::startXmlBlock( string& t )
    {
        fXmlStack.push(t);
        fFile << "<" << t << ">\n";
    }

    void XmlCacheFormat::endXmlBlock()
    {
        string block = fXmlStack.top();
        fFile << "</" << block << ">\n";
        fXmlStack.pop();
    }

    void XmlCacheFormat::writeXmlTagValue( string& tag, string value )
    {
        for (unsigned int i = 0; i < fXmlStack.size(); i++ )
            fFile << "\t";

        fFile << "<" << tag << "> ";    // Extra space is important for reading
        fFile << value;
        fFile << " </" << tag << ">\n"; // Extra space is important for reading
    }

    void XmlCacheFormat::writeXmlTagValue( string& tag, int value )
    {
        for (unsigned int i = 0; i < fXmlStack.size(); i++ )
            fFile << "\t";

        fFile << "<" << tag << "> ";    // Extra space is important for reading
        ostringstream oss;
        oss << value;
        fFile << oss.str();
        fFile << " </" << tag << ">\n"; // Extra space is important for reading
    }

    bool XmlCacheFormat::readXmlTagValue( string tag, MStringArray& values )
    {
        string endTag = XMLENDTAG(tag);
        bool status = true;

        values.clear();

        // Yes this could be much much smarter
        if( findXmlStartTag(tag) )
        {
            string token;
            fFile >> token;
            while ( !fFile.eof() && token != endTag )
            {
                values.append( token.data() );
                fFile >> token;
            }
        }
        else
        {
            status = false;
        }

        return status;
    }

    bool XmlCacheFormat::readXmlTagValueInChunk( string tag, MStringArray& values )
    {
        string endTag = XMLENDTAG(tag);
        bool status = true;

        values.clear();

        // Find the tag in the currently read chunk.
        if( findXmlStartTagInChunk(tag) )
        {
            string token;
            fFile >> token;
            // Look for the values within the bounds of the tag.
            while ( !fFile.eof() && token != endTag )
            {
                values.append( token.data() );
                fFile >> token;
            }
        }
        else
        {
            status = false;
        }

        return status;
    }

    void XmlCacheFormat::readXmlTag( string& value )
    {
        value.clear();
            fFile >> value;
    }

    bool XmlCacheFormat::findXmlStartTag( string& tag )
    {
        string tagRead;
        string tagExpected = XMLSTARTTAG(tag);

        fFile >> tagRead;
        // Keep looking all the way to EOF
        while(!fFile.eof() && tagRead != tagExpected)
        {
            fFile >> tagRead;
        }
        if( tagRead != tagExpected )
        {
        }

        return ( tagRead == tagExpected );
    }

    bool XmlCacheFormat::findXmlStartTagInChunk( string& tag )
    //
    // Look for the given tag within the currently read chunk.
    //
    {
        string tagRead;
        string tagExpected = XMLSTARTTAG(tag);
        string tagEndChunk("</"+chunkTag+">");

        fFile >> tagRead;

        // Keep looking all the way to EOF
        while((!fFile.eof()) && (tagRead != tagExpected) && (tagRead != tagEndChunk))
        {
            fFile >> tagRead;
        }
        if( (tagRead != tagExpected) && (tagRead != tagEndChunk) )
        {
        }

        return ( tagRead == tagExpected );
    }

    bool XmlCacheFormat::findXmlEndTag(string& tag)
    {
        string tagRead;
        string tagExpected("</"+tag+">");

        fFile >> tagRead;
        if( tagRead != tagExpected )
        {
        }

        return ( tagRead == tagExpected );
    }

    void XmlCacheFormat::writeXmlValue( string& value )
    {
        fFile << value << " ";
    }

    void XmlCacheFormat::writeXmlValue( double value )
    {
        fFile << value << " ";
    }

    void XmlCacheFormat::writeXmlValue( float value )
    {
        fFile << value << " ";
    }

    void XmlCacheFormat::writeXmlValue( int value )
    {
        fFile << value << " ";
    }

    // ****************************************

    MStatus initializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj, PLUGIN_COMPANY, "1.0", "Any" );

        plugin.registerCacheFormat(
            XmlCacheFormat::translatorName(),
            XmlCacheFormat::creator
        );

        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj );

        plugin.deregisterCacheFormat( XmlCacheFormat::translatorName() );

        return MS::kSuccess;
    }