#include "gpuCacheCmd.h"
#include "gpuCacheShapeNode.h"
#include "gpuCacheStrings.h"
#include "gpuCacheUtil.h"
#include "gpuCacheConfig.h"
#include "gpuCacheVBOProxy.h"
#include "gpuCacheVramQuery.h"
#include "gpuCacheMaterialBakers.h"
#include "gpuCacheSubSceneOverride.h"
#include "gpuCacheUnitBoundingBox.h"
#include "CacheWriter.h"
#include "CacheReader.h"
#include "gpuCacheGeometry.h"
#include <maya/MArgList.h>
#include <maya/MAnimControl.h>
#include <maya/MDagPathArray.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MFnNurbsSurface.h>
#include <maya/MFnSet.h>
#include <maya/MFnSubd.h>
#include <maya/MGlobal.h>
#include <maya/MPlugArray.h>
#include <maya/MSyntax.h>
#include <maya/MBoundingBox.h>
#include <maya/MFileObject.h>
#include <maya/MItDag.h>
#include <maya/MVector.h>
#include <maya/MFnTransform.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MFnComponentListData.h>
#include <maya/MFnLambertShader.h>
#include <maya/MViewport2Renderer.h>
#include <maya/MDagPath.h>
#include <maya/MPointArray.h>
#include <maya/MUintArray.h>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/unordered_set.hpp>
#include <cfloat>
#include <limits>
#include <list>
#include <iomanip>
#include <map>
#include <sstream>
#include <fstream>
#define MStatError(status,msg)                              \
    if ( MS::kSuccess != (status) ) {                       \
        MPxCommand::displayError(                           \
            (msg) + MString(":") + (status).errorString()); \
        return (status);                                    \
    }
#define MStatErrorNullObj(status,msg)                       \
    if ( MS::kSuccess != (status) ) {                       \
        MPxCommand::displayError(                           \
            (msg) + MString(":") + (status).errorString()); \
        return MObject::kNullObj;                           \
    }
#define MCheckReturn(expression)                \
    {                                           \
        MStatus status = (expression);          \
        if ( MS::kSuccess != (status) ) {       \
            return (status);                    \
        }                                       \
    }
#define MUpdateProgressAndCheckInterruption(progressBar)    \
    {                                                       \
        (progressBar).stepProgress();                       \
        if ((progressBar).isCancelled()) {                  \
            return MS::kFailure;                            \
        }                                                   \
    }                                                       \
namespace {
using namespace GPUCache;
    
    
    
    
    
    
    
    
    
    
    
    boost::shared_ptr<CacheWriter> createWriter(
const MFileObject& targetFile,
                                                const char         compressLevel,
    {
        
        
        if (!cacheDirectory.
exists()) {
 
            
        }
        
        
            
            
            bool writeable;
            {
                std::ofstream ofs(resolvedFullName.
asChar());
                writeable = ofs.is_open();
            }
            
            if (!writeable) {
                msg.
format(fmt, resolvedFullName);
                return boost::shared_ptr<CacheWriter>();
            }
            
            if (
remove(resolvedFullName.
asChar()) != 0) {
 
                msg.
format(fmt, resolvedFullName);
                return boost::shared_ptr<CacheWriter>();
            }
        }
        
        
        
        boost::shared_ptr<CacheWriter> cacheWriter = 
            CacheWriter::create("Alembic", targetFile, compressLevel, dataFormat);
        if (!cacheWriter) {
            return boost::shared_ptr<CacheWriter>();
        }
        if (!cacheWriter->valid()) {
            
            cacheWriter.reset();
            return boost::shared_ptr<CacheWriter>();
        }
        return cacheWriter;
    }
    bool isPlugConnectedToTexture2d(
const MPlug& plug)
 
    {
            assert(connections.
length() == 1);
            
                return false;
            MObject srcNode = connections[0].node();
 
        }
        return false;
    }
    {
            assert(connections.
length() == 1);
            
            assert(!diffusePlugR.
isNull());
            assert(!diffusePlugG.
isNull());
            assert(!diffusePlugB.
isNull());
            float r = diffusePlugR.
asFloat(MDGContext::fsNormal, &statusR);
 
            float g = diffusePlugG.
asFloat(MDGContext::fsNormal, &statusG);
 
            float b = diffusePlugB.
asFloat(MDGContext::fsNormal, &statusB);
 
        }
    }
    bool isPlugConnectedToTextureNode(
const MPlug& plug)
 
    {
            assert(connections.
length() == 1);
            
                return false;
            MObject srcNode = connections[0].node();
 
                return true;
            }
        }
        return false;
    }
    MStatus getShapeDiffuseColors(
const std::vector<MDagPath>& paths,
 
                                  std::vector<MColor>& diffuseColors)
    {
        diffuseColors.resize(paths.size(), Config::kDefaultGrayColor);
        
        for (size_t pathIndex = 0; pathIndex < paths.size(); pathIndex++) {
            
            assert(!instObjectGroupsParent.
isNull());
                paths[pathIndex].instanceNumber());
            assert(!instObjectGroups.isNull());
            
            if (instObjectGroups.isConnected()) {
                
                instObjectGroups.connectedTo(dstPlugs, false, true, &status);
                if (status && dstPlugs.
length() > 0) {
 
                    
                    shadingGroup = dstPlugs[0].node();
                }
            }
            
            
            MPlug objectGroupsParent = instObjectGroups.
child(0);
 
            assert(!objectGroupsParent.
isNull());
            for (unsigned int parts = 0;
                parts++) {
                    MPlug objectGroups = objectGroupsParent[parts];
 
                    
                        
                        objectGroups.
connectedTo(dstPlugs, 
false, 
true, &status);
                        if (status && dstPlugs.
length() > 0) {
 
                            
                            shadingGroup = dstPlugs[0].node();
                        }
                    }
            } 
                
                
                MPlug surfaceShaderPlug = shadingEngine.
findPlug(
"surfaceShader");
 
                assert(!surfaceShaderPlug.
isNull());
                
                    surfaceShaderPlug.
connectedTo(srcPlugs, 
true, 
false, &status);
                    if (status && srcPlugs.
length() > 0) {
 
                        
                        shaderObj = srcPlugs[0].node();
                    }
                }
            }
                MColor diffuseColor = Config::kDefaultGrayColor;
 
                MColor transparency = Config::kDefaultTransparency;
 
                
                    assert(!diffusePlug.isNull());
                    assert(!transparencyPlug.isNull());
                    if (isPlugConnectedToTexture2d(colorPlug)) {
                        diffuseColor = getTexture2dDefaultColor(colorPlug);
                    }
                    else if (!isPlugConnectedToTextureNode(colorPlug)) {
                        diffuseColor = lambert.
color();
                    }
                    if (!isPlugConnectedToTextureNode(diffusePlug)) {
                    }
                    if (!isPlugConnectedToTextureNode(transparencyPlug)) {
                    }
                }
                
                diffuseColor.
a = 1.0f - (transparency.
r * 0.3f +
                    transparency.
g * 0.59f + transparency.
b * 0.11f);
                diffuseColors[pathIndex] = diffuseColor;
            }
        }
    }
    {
            "basenameEx(`file -q -sceneName`)");
        if (sceneName.
length() == 0) {
 
        }
        return sceneName;
    }
    MString getSceneNameAsValidObjectName()
 
    {
    }
    size_t maxNumVerts(const ShapeData::Ptr& geom)
    {
        size_t maxNumVerts = 0;
        BOOST_FOREACH(
            const ShapeData::SampleMap::value_type& smv, geom->getSamples()
        ) {
            maxNumVerts = std::max(maxNumVerts, smv.second->numVerts());
        }
        return maxNumVerts;
    }
    double toHumanUnits(MUint64 bytes, 
MString& units)
 
    {
        const MUint64 KB = 1024;
        const MUint64 MB = 1024 * KB;
        const MUint64 GB = 1024 * MB;
        const MUint64 TB = 1024 * GB;
        double  value;
        if (bytes >= TB) {
            units = "TB";
            value =  double(bytes)/TB;
        }
        else if (bytes >= GB) {
            units = "GB";
            value =  double(bytes)/GB;
        }
        else if (bytes >= MB) {
            units = "MB";
            value =  double(bytes)/MB;
        }
        else if (bytes >= KB) {
            units = "KB";
            value =  double(bytes)/KB;
        }
        else {
            units = "bytes";
            value =  double(bytes);
        }
        return value;
    }
    
    
    
    class Baker
    {
    public:
        static boost::shared_ptr<Baker> create(
const MObject&               
object,
 
                                               const std::vector<MDagPath>& paths);
        static bool isBakeable(
const MObject& dagNode);
 
        Baker(
const MObject& 
object, 
const std::vector<MDagPath>& paths)
            : fNode(object), fPaths(paths)
        {}
        virtual ~Baker() {}
        virtual const SubNode::MPtr getNode(size_t instIndex) const = 0;
        virtual void setWriteMaterials() {}
        virtual void setUseBaseTessellation() {}
    protected:
        std::vector<MDagPath> fPaths;
    };
    
    
    
    
    
    class ShapeBaker : public Baker
    {
    public:
        virtual ~ShapeBaker() {}
        void enableUVs() 
        {
            fCacheMeshSampler->enableUVs();
        }
        
        {
            
            MCheckReturn( sampleTopologyAndAttributes() );
            
            std::vector<MColor> diffuseColors;
            MCheckReturn( getShapeDiffuseColors(fPaths, diffuseColors) );
            bool diffuseColorsAnimated = (fPrevDiffuseColors != diffuseColors);
            
            if (fCacheMeshSampler->isAnimated() || diffuseColorsAnimated) {
                for (size_t i = 0; i < fGeometryInstances.size(); i++) {
                    fGeometryInstances[i]->addSample(
                        fCacheMeshSampler->getSample(
                            diffuseColors[i]));
                }
            }
            fPrevDiffuseColors.swap(diffuseColors);
        }
        
        virtual const SubNode::MPtr getNode(size_t instIndex) const
        {
            return SubNode::create(
                fNode.name(),
                fGeometryInstances[instIndex]
            );
        }
    protected:
        ShapeBaker(
const MObject& node, 
const std::vector<MDagPath>& paths)
            : Baker(node, paths),
              fCacheMeshSampler(
                  
                  CacheMeshSampler::create(!Config::isIgnoringUVs())),
              fGeometryInstances(paths.size())
        {
            
            for (size_t i = 0; i < paths.size(); i++) {
                fGeometryInstances[i] = ShapeData::create();
            }
        }
        virtual void setWriteMaterials()
        {
            
            for (size_t i = 0; i < fPaths.size(); i++) {
                
                InstanceMaterialLookup lookup(fPaths[i]);
                if (lookup.hasWholeObjectMaterial()) {
                    
                    MObject material = lookup.findWholeObjectSurfaceMaterial();
 
                        surfaceMaterial = dgMaterial.
name();
                    }
                }
                else if (lookup.hasComponentMaterials()) {
                    
                    std::vector<MObject> materials;
                    lookup.findSurfaceMaterials(materials);
                    
                    
                    BOOST_FOREACH (
const MObject& material, materials) {
                            surfaceMaterial = dgMaterial.
name();
                            break;
                        }
                    }
                }
                if (surfaceMaterial.
length() > 0) {
 
                    fGeometryInstances[i]->setMaterial(surfaceMaterial);
                }
            }
        }
        virtual void setUseBaseTessellation()
        {
            fCacheMeshSampler->setUseBaseTessellation();
        }
        virtual MStatus sampleTopologyAndAttributes() = 0;
 
    private:
        
        ShapeBaker(const ShapeBaker&);
        const ShapeBaker& operator=(const ShapeBaker&);
    protected:
        const boost::shared_ptr<CacheMeshSampler> fCacheMeshSampler;
        std::vector<MColor>                       fPrevDiffuseColors;
        std::vector<ShapeData::MPtr>              fGeometryInstances;
    };
    
    
    
    
    class XformBaker : public Baker
    {
    public:
        virtual ~XformBaker()
        {}
        XformBaker(
const MObject& xformNode, 
const std::vector<MDagPath>& xformPaths)
            : Baker(xformNode, xformPaths),
              fCacheXformSamplers(CacheXformSampler::create(xformNode)),
              fXformInstances(xformPaths.size())
        {
            for (size_t i = 0; i < fXformInstances.size(); i++) {
                fXformInstances[i] = XformData::create();
            }
        }
        {
            fCacheXformSamplers->addSample();
            if (fCacheXformSamplers->isAnimated()) {
                for (size_t i = 0; i < fXformInstances.size(); i++) {
                    fXformInstances[i]->addSample(
                }
            }
        }
        virtual const SubNode::MPtr getNode(size_t instIndex) const
        {
            return SubNode::create(
                fNode.name(),
                fXformInstances[instIndex]
            );
        }
    private:
        boost::shared_ptr<CacheXformSampler> fCacheXformSamplers;
        std::vector<XformData::MPtr>         fXformInstances;
    };
    
    
    
    
    class MeshDataBaker : public ShapeBaker
    {
    public:
        virtual ~MeshDataBaker() {}
    protected:
        virtual MStatus sampleTopologyAndAttributes()
 
        {
            MObject meshData = getMeshData(&status);
 
            MStatError(status, "getMeshData()");
            bool shapeVisibility = ShapeVisibilityChecker(fNode.object()).isVisible();
            
            return fCacheMeshSampler->addSample(meshData, shapeVisibility) ?
        }
        MeshDataBaker(
const MObject& shapeNode, 
const std::vector<MDagPath>& shapePaths)
            : ShapeBaker(shapeNode, shapePaths)
        {}
    private:
        
        MeshDataBaker(const MeshDataBaker&);
        const MeshDataBaker& operator=(const MeshDataBaker&);
    };
    
    
    
    class MeshBaker : public ShapeBaker
    {
    public:
        MeshBaker(
const MObject& meshNode, 
const std::vector<MDagPath>& meshPaths)
            : ShapeBaker(meshNode, meshPaths), fMeshNode(meshNode)
        {}
        virtual ~MeshBaker() {}
    protected:
        virtual MStatus sampleTopologyAndAttributes()
 
        {
            return fCacheMeshSampler->addSampleFromMesh(fMeshNode) ?
        }
    private:
        
        MeshBaker(const MeshBaker&);
        const MeshBaker& operator=(const MeshBaker&);
    };
    
    
    
    class NurbsBaker : public MeshDataBaker
    {
    public:
        NurbsBaker(
const MObject& nurbsNode, 
const std::vector<MDagPath>& nurbsPaths)
            : MeshDataBaker(nurbsNode, nurbsPaths)
        {
            
        }
    protected:
        {
            modifier.
connect(nurbsNode.findPlug(
"explicitTessellationAttributes"),
                             tessellatorNode.
findPlug(
"explicitTessellationAttributes"));
            modifier.
connect(nurbsNode.findPlug(
"curvatureTolerance"),
                             tessellatorNode.
findPlug(
"curvatureTolerance"));
            modifier.
connect(nurbsNode.findPlug(
"uDivisionsFactor"),
                             tessellatorNode.
findPlug(
"uDivisionsFactor"));
            modifier.
connect(nurbsNode.findPlug(
"vDivisionsFactor"),
                             tessellatorNode.
findPlug(
"vDivisionsFactor"));
            modifier.
connect(nurbsNode.findPlug(
"modeU"),
            modifier.
connect(nurbsNode.findPlug(
"modeV"),
            modifier.
connect(nurbsNode.findPlug(
"numberU"),
            modifier.
connect(nurbsNode.findPlug(
"numberV"),
            modifier.
connect(nurbsNode.findPlug(
"useChordHeight"),
                             tessellatorNode.
findPlug(
"useChordHeight"));
            modifier.
connect(nurbsNode.findPlug(
"useChordHeightRatio"),
                             tessellatorNode.
findPlug(
"useChordHeightRatio"));
            modifier.
connect(nurbsNode.findPlug(
"chordHeight"),
                             tessellatorNode.
findPlug(
"chordHeight"));
            modifier.
connect(nurbsNode.findPlug(
"chordHeightRatio"),
                             tessellatorNode.
findPlug(
"chordHeightRatio"));
            modifier.
connect(nurbsNode.findPlug(
"smoothEdge"),
                             tessellatorNode.
findPlug(
"smoothEdge"));
            modifier.
connect(nurbsNode.findPlug(
"smoothEdgeRatio"),
                             tessellatorNode.
findPlug(
"smoothEdgeRatio"));
            modifier.
connect(nurbsNode.findPlug(
"edgeSwap"),
            modifier.
connect(nurbsNode.findPlug(
"local"),
                             tessellatorNode.
findPlug(
"inputSurface"));
            
            
            return mesh;
        }
    };
    
    
    
    class SubdBaker : public MeshDataBaker
    {
    public:
        SubdBaker(
const MObject& subdNode, 
const std::vector<MDagPath>& subdPaths)
            : MeshDataBaker(subdNode, subdPaths)
        {}
    protected:
        {
            if (!*status) return meshData.object();
            int format = -1;
            int depth = -1;
            int sampleCount = -1;
            MPlug formatPlug = subdNode.findPlug(
"format");
 
            MPlug depthPlug = subdNode.findPlug(
"depth");
 
            MPlug sampleCountPlug = subdNode.findPlug(
"sampleCount");
 
            subdNode.tesselate(
                format==0, depth, sampleCount, meshData.object(), status);
            return meshData.object();
        }
    };
    
    
    
    
    class RecursiveBaker : public Baker
    {
    public:
        RecursiveBaker(
const MObject& shapeNode, 
const std::vector<MDagPath>& shapePaths)
            : Baker(shapeNode, shapePaths)
        {
            
            MPxNode* userNode = fNode.userNode();
 
            assert(userNode);
            ShapeNode* bakedNode = userNode ?
                dynamic_cast<ShapeNode*>(userNode) : NULL;
            assert(bakedNode);
            
            if (bakedNode) {
                GlobalReaderCache::theCache().waitForRead(bakedNode->getCacheFileEntry().get());
                fSrcTopNode = bakedNode->getCachedGeometry();
                if (fSrcTopNode) {
                    fSampleReplicator.reset(new SampleReplicator);
                    fSrcTopNode->accept(*fSampleReplicator);
                }
            }
        }
        virtual ~RecursiveBaker()
        {}
        {
            if (!fSrcTopNode) {
            }
            return fSampleReplicator->sample(time);
        }
        virtual const SubNode::MPtr getNode(size_t instIndex) const
        {
            
            if (fSrcTopNode && !fDstTopNode) {
                
                
                HierarchyReplicator hierarchyReplicator(fSampleReplicator);
                fSrcTopNode->accept(hierarchyReplicator);
                RecursiveBaker* nonConstThis = const_cast<RecursiveBaker*>(this);
                nonConstThis->fDstTopNode = hierarchyReplicator.dstSubNode();
            }
            return fDstTopNode;
        }
    private:
        
        RecursiveBaker(const RecursiveBaker&);
        const RecursiveBaker& operator=(const RecursiveBaker&);
        class SampleReplicator : public SubNodeVisitor
        {
        public:
            typedef boost::shared_ptr<SampleReplicator> MPtr;
            SampleReplicator()
            {}
            virtual void visit(const XformData& srcXform,
                               const SubNode&   srcSubNode)
            {
                
                XformData::MPtr dstXform = XformData::create();
                fXforms[&srcXform] = std::make_pair(
                        dstXform, boost::shared_ptr<const XformSample>());
                
                BOOST_FOREACH(const SubNode::Ptr& srcChild, srcSubNode.getChildren()) {
                    srcChild->accept(*this);
                }
            }
            virtual void visit(const ShapeData& srcShape,
                               const SubNode&   srcSubNode)
            {
                
                ShapeData::MPtr dstShape = ShapeData::create();
                dstShape->setMaterials(srcShape.getMaterials());
                fShapes[&srcShape] = std::make_pair(
                        dstShape, boost::shared_ptr<const ShapeSample>());
            }
            {
                BOOST_FOREACH(XformMapping::value_type& xform, fXforms) {
                    
                    boost::shared_ptr<const XformSample> srcXformSample =
                        xform.first->getSample(time);
                    
                    if (xform.second.second != srcXformSample) {
                        
                        boost::shared_ptr<XformSample> dstXformSample =
                            XformSample::create(
                                srcXformSample->xform(),
                                srcXformSample->boundingBox(),
                                srcXformSample->visibility());
                        xform.second.first->addSample(dstXformSample);
                        xform.second.second = srcXformSample;
                    }
                }
                BOOST_FOREACH(ShapeMapping::value_type& shape, fShapes) {
                    
                    boost::shared_ptr<const ShapeSample> srcShapeSample =
                        shape.first->getSample(time);
                    
                    if (shape.second.second != srcShapeSample) {
                        
                        boost::shared_ptr<ShapeSample> dstShapeSample =
                            ShapeSample::create(
                                srcShapeSample->numWires(),
                                srcShapeSample->numVerts(),
                                srcShapeSample->wireVertIndices(),
                                srcShapeSample->triangleVertexIndexGroups(),
                                srcShapeSample->positions(),
                                srcShapeSample->boundingBox(),
                                srcShapeSample->diffuseColor(),
                                srcShapeSample->visibility());
                        if (srcShapeSample->normals()) {
                            dstShapeSample->setNormals(srcShapeSample->normals());
                        }
                        if (srcShapeSample->uvs()) {
                            dstShapeSample->setUVs(srcShapeSample->uvs());
                        }
                        shape.second.first->addSample(dstShapeSample);
                        shape.second.second = srcShapeSample;
                    }
                }
            }
            XformData::MPtr xform(const XformData& xform)
            {
                XformMapping::iterator iter = fXforms.find(&xform);
                assert(iter != fXforms.end());
                return (*iter).second.first;
            }
            ShapeData::MPtr shape(const ShapeData& shape)
            {
                ShapeMapping::iterator iter = fShapes.find(&shape);
                assert(iter != fShapes.end());
                return (*iter).second.first;
            }
        private:
            
            SampleReplicator(const SampleReplicator&);
            const SampleReplicator& operator=(const SampleReplicator&);
            typedef std::pair<XformData::MPtr,boost::shared_ptr<const XformSample> > XformWithPrev;
            typedef std::pair<ShapeData::MPtr,boost::shared_ptr<const ShapeSample> > ShapeWithPrev;
            typedef std::map<const XformData*,XformWithPrev> XformMapping;
            typedef std::map<const ShapeData*,ShapeWithPrev> ShapeMapping;
            XformMapping fXforms;
            ShapeMapping fShapes;
        };
        class HierarchyReplicator : public SubNodeVisitor
        {
        public:
            HierarchyReplicator(SampleReplicator::MPtr sampleReplicator)
                : fSampleReplicator(sampleReplicator)
            {}
            virtual void visit(const XformData& srcXform,
                               const SubNode&   srcSubNode)
            {
                
                
                XformData::MPtr dstXform = fSampleReplicator->xform(srcXform);
                fDstSubNode = SubNode::create(srcSubNode.getName() != "|" ?
                    srcSubNode.getName() : "top", dstXform);
                
                BOOST_FOREACH(const SubNode::Ptr& srcChild, srcSubNode.getChildren())
                {
                    HierarchyReplicator replicator(fSampleReplicator);
                    srcChild->accept(replicator);
                    SubNode::connect(fDstSubNode, replicator.dstSubNode());
                }
            }
            virtual void visit(const ShapeData& srcShape,
                               const SubNode&   srcSubNode)
            {
                
                ShapeData::MPtr dstShape = fSampleReplicator->shape(srcShape);
                fDstSubNode = SubNode::create(srcSubNode.getName(), dstShape);
            }
            SubNode::MPtr dstSubNode() const
            { return fDstSubNode; }
        private:
            
            HierarchyReplicator(const HierarchyReplicator&);
            const HierarchyReplicator& operator=(const HierarchyReplicator&);
            SampleReplicator::MPtr fSampleReplicator;
            SubNode::MPtr fDstSubNode;
        };
        SubNode::Ptr            fSrcTopNode;
        SubNode::MPtr           fDstTopNode;
        SampleReplicator::MPtr  fSampleReplicator;
    };
    
    
    
    bool Baker::isBakeable(
const MObject& dagNode)
 
    {
        {
            return true;
        }
        return false;
    }
    boost::shared_ptr<Baker> Baker::create(
const MObject&               shapeNode,
                                           const std::vector<MDagPath>& shapePaths)
    {
            return boost::make_shared<XformBaker>(shapeNode, shapePaths);
        }
            return boost::make_shared<MeshBaker>(shapeNode, shapePaths);
        }
            return boost::make_shared<NurbsBaker>(shapeNode, shapePaths);
        }
            return boost::make_shared<SubdBaker>(shapeNode, shapePaths);
        }
        if (shape.
typeId() == ShapeNode::id) {
 
            return boost::make_shared<RecursiveBaker>(shapeNode, shapePaths);
        }
        assert(false);
        return boost::shared_ptr<Baker>();
    }
    
    
    
    class Writer : boost::noncopyable
    {
    public:
        Writer(const char       compressLevel,
               const MTime&     timePerCycle,
 
          : fCompressLevel(compressLevel),
            fDataFormat(dataFormat),
        {}
        
        MStatus writeNode(
const SubNode::Ptr&           subNode,
 
                          const MaterialGraphMap::Ptr&  materials,
        {
            boost::shared_ptr<CacheWriter> writer = createWriter(
                targetFile,
                fCompressLevel,
                fDataFormat
            );
            if (!writer) {
            }
            writer->writeSubNodeHierarchy(
                subNode,
                fTimePerCycleInSeconds, fStartTimeInSeconds);
            if (materials) {
                writer->writeMaterials(
                    materials,
                    fTimePerCycleInSeconds, fStartTimeInSeconds);
            }
        }
        
        MStatus writeNodes(
const std::vector<SubNode::Ptr>& subNodes,
 
                           const MaterialGraphMap::Ptr&     materials,
        {
            boost::shared_ptr<CacheWriter> writer = createWriter(
                targetFile,
                fCompressLevel,
                fDataFormat
            );
            if (!writer) {
            }
            BOOST_FOREACH(const SubNode::Ptr& subNode, subNodes) {
                writer->writeSubNodeHierarchy(
                    subNode,
                    fTimePerCycleInSeconds,
                    fStartTimeInSeconds);
            }
            if (materials) {
                writer->writeMaterials(materials,
                    fTimePerCycleInSeconds, fStartTimeInSeconds);
            }
        }
    private:
        const char     fCompressLevel;
        const double   fTimePerCycleInSeconds;
        const double   fStartTimeInSeconds;
    };
    
    
    
    class Stat
    {
    public:
        Stat(MUint64 bytesPerUnit)
            : fMin(std::numeric_limits<MUint64>::max()),
              fMax(0),
              fTotal(0),
              fInstancedTotal(0),
              fBytesPerUnit(bytesPerUnit)
        {}
        void addSample(
            const boost::shared_ptr<const IndexBuffer> buffer, const int indicesPerElem)
        {
            addSample(buffer->numIndices() / indicesPerElem, buffer.get());
        };
        void addSample(
            const boost::shared_ptr<const VertexBuffer> buffer)
        {
            addSample(buffer->numVerts(), buffer.get());
        };
        void addSample(
        {
            addSample(numIndices, (void*)buffer);
        }
        void addSample(
        {
            addSample(numVertices, (void*)buffer);
        }
        void addSample(
            const boost::shared_ptr<const VBOBuffer> buffer, size_t numPrimitives)
        {
            addSample(numPrimitives, buffer.get());
        }
        MUint64 getNbSamples() const        { return fUniqueEntries.size(); }
        MUint64 getMin() const              { return fMin;}
        MUint64 getMax() const              { return fMin;}
        MUint64 getTotal() const            { return fTotal;}
        MUint64 getInstancedTotal() const   { return fInstancedTotal;}
        double getAverage() const {
            return double(getTotal())/double(getNbSamples());
        }
        MUint64 getSize() const {
            return fTotal * fBytesPerUnit;
        }
        {
            if (getNbSamples() == 0) {
                    name);
                result = msg;
            }
            else {
                double  memSize = toHumanUnits(getSize(), memUnit);
                MString msg_buffers; msg_buffers += (double)getNbSamples();
 
                MString msg_avrg;    msg_avrg    += (double)getAverage();
 
                MString msg_min;     msg_min     += (double)fMin;
 
                MString msg_max;     msg_max     += (double)fMax;
 
                MString msg_total;   msg_total   += (double)fTotal;
 
                MString msg_memSize; msg_memSize += memSize;
 
                    name, msg_buffers, msg_avrg,
                    msg_min, msg_max, msg_total, msg_memSize, memUnit);
                result = msg;
            }
            return result;
        }
    private:
        void addSample(MUint64 value, const void* buffer)
        {
            if (fUniqueEntries.insert(buffer).second) {
                fMin = std::min(fMin, value);
                fMax = std::max(fMax, value);
                fTotal += value;
            }
            fInstancedTotal += value;
        };
        boost::unordered_set<const void*> fUniqueEntries;
        MUint64         fMin;
        MUint64         fMax;
        MUint64         fTotal;
        const MUint64   fBytesPerUnit;
        
        MUint64         fInstancedTotal;
    };
    
    
    
    class Stats
    {
    public:
        Stats()
            : fNbNodes(0),
              fNbSubNodes(0),
              fWires(     2 * sizeof(IndexBuffer::index_t)),
              fTriangles( 3 * sizeof(IndexBuffer::index_t)),
              fVerts(     3 * sizeof(float)),
              fNormals(   3 * sizeof(float)),
              fUVs(       2 * sizeof(float)),
              fVP2Index(  sizeof(IndexBuffer::index_t)),
              fVP2Vertex( sizeof(float)),
              fVBOIndex(  sizeof(IndexBuffer::index_t)),
              fVBOVertex( sizeof(float)),
              fNbMaterialGraphs(0),
              fNbMaterialNodes(0)
        {}
        void accumulateNode()
        {
            ++fNbNodes;
        }
        void accumulateMaterialGraph(const MaterialGraph::Ptr&)
        {
            ++fNbMaterialGraphs;
        }
        void accumulateMaterialNode(const MaterialNode::Ptr&)
        {
            ++fNbMaterialNodes;
        }
        void accumulate(const ShapeData& shape)
        {
            ++fNbSubNodes;
            BOOST_FOREACH(const ShapeData::SampleMap::value_type& v,
                          shape.getSamples()) {
                accumSample(v.second);
            }
        }
        void accumulate(const ShapeData& shape,
        {
            ++fNbSubNodes;
            accumSample(shape.getSample(time));
        }
        void print(
MStringArray& result, 
bool printInstancedInfo)
 const 
        {
            {
                MString msg_nbGeom;     msg_nbGeom     += fNbNodes;
 
                MString msg_nbSubNodes; msg_nbSubNodes += fNbSubNodes;
 
                    msg_nbGeom, msg_nbSubNodes);
            }
            if (printInstancedInfo) {
                MString msgInstWires; msgInstWires += (double)fWires.getInstancedTotal();
 
                MString msgInstTris;  msgInstTris  += (double)fTriangles.getInstancedTotal();
 
                    msgInstWires, msgInstTris);
            }
            {
                MUint64 totalMem = (fWires.getSize() +
                                    fTriangles.getSize() +
                                    fVerts.getSize() +
                                    fNormals.getSize() +
                                    fUVs.getSize());
                double  memSize = toHumanUnits(totalMem, memUnit);
                MString msg_memSize; msg_memSize += memSize;
 
                    msg_memSize, memUnit);
            }
            {
                MUint64 totalMem = (fVBOIndex.getSize() + fVBOVertex.getSize());
                if (Config::vp2OverrideAPI() != Config::kMPxDrawOverride) {
                    totalMem += (fVP2Index.getSize() + fVP2Vertex.getSize());
                }
                double  memSize = toHumanUnits(totalMem, memUnit);
                MString msg_memSize; msg_memSize += memSize;
 
                    msg_memSize, memUnit);
            }
            {
                msg_nbGraphs += fNbMaterialGraphs;
                msg_nbNodes  += fNbMaterialNodes;
                    msg_nbGraphs, msg_nbNodes);
            }
        }
    private:
        
        void accumSample(const boost::shared_ptr<const ShapeSample>& sample) {
            accumIndexBuffer(fWires, sample->wireVertIndices(), 2);
            for(size_t i=0; i<sample->numIndexGroups(); ++i) {
                accumIndexBuffer(fTriangles, sample->triangleVertIndices(i), 3);
            }
            accumVertexBuffer(fVerts, sample->positions());
            accumVertexBuffer(fNormals, sample->normals());
            accumVertexBuffer(fUVs, sample->uvs());
        }
        void accumIndexBuffer(
            Stat& stat,
            const boost::shared_ptr<const IndexBuffer> indexBuffer,
            const int indicesPerElem
        )
        {
            if (indexBuffer && indexBuffer != UnitBoundingBox::indices()) {
                stat.addSample(indexBuffer, indicesPerElem);
                {
                        SubSceneOverride::lookup(indexBuffer);
                    if (vp2Buffer)
                        fVP2Index.addSample(vp2Buffer, indexBuffer->numIndices());
                }
                {
                    boost::shared_ptr<const VBOBuffer> vboBuffer =
                        VBOBuffer::lookup(indexBuffer);
                    if (vboBuffer)
                        fVBOIndex.addSample(vboBuffer, indexBuffer->numIndices());
                }
            }
        }
        void accumVertexBuffer(
            Stat& stat,
            const boost::shared_ptr<const VertexBuffer> vertexBuffer
        )
        {
            if (vertexBuffer && vertexBuffer != UnitBoundingBox::positions()) {
                stat.addSample(vertexBuffer);
                {
                        SubSceneOverride::lookup(vertexBuffer);
                    if (vp2Buffer)
                        fVP2Vertex.addSample(
                            vp2Buffer, 3 * vertexBuffer->numVerts());
                }
                {
                    boost::shared_ptr<const VBOBuffer> vboBuffer =
                        VBOBuffer::lookup(vertexBuffer);
                    if (vboBuffer)
                        fVBOVertex.addSample(
                            vboBuffer, 3 * vertexBuffer->numVerts());
                }
                {
                    boost::shared_ptr<const VBOBuffer> vboBuffer =
                        VBOBuffer::lookupFlippedNormals(vertexBuffer);
                    if (vboBuffer)
                        fVBOVertex.addSample(
                            vboBuffer, 3 * vertexBuffer->numVerts());
                }
            }
        }
        
        int  fNbNodes;
        int  fNbSubNodes;
        Stat fWires;
        Stat fTriangles;
        Stat fVerts;
        Stat fNormals;
        Stat fUVs;
        Stat fVP2Index;
        Stat fVP2Vertex;
        Stat fVBOIndex;
        Stat fVBOVertex;
        int  fNbMaterialGraphs;
        int  fNbMaterialNodes;
    };
    
    
    
    class StatsVisitor : public SubNodeVisitor
    {
    public:
        StatsVisitor() : fAtGivenTime(false) {}
        StatsVisitor(
MTime time) : fAtGivenTime(
true), fTime(time) {}
        void accumulateNode(const SubNode::Ptr& topNode)
        {
            fStats.accumulateNode();
            if (topNode) {
                topNode->accept(*this);
            }
        }
        void accumulateMaterialGraph(const MaterialGraphMap::Ptr& materials)
        {
            if (materials) {
                BOOST_FOREACH (
                        const MaterialGraphMap::NamedMap::value_type& val,
                        materials->getGraphs()) {
                    fStats.accumulateMaterialGraph(val.second);
                    accumulateMaterialNode(val.second);
                }
            }
        }
        void accumulateMaterialNode(const MaterialGraph::Ptr& material)
        {
            if (material) {
                BOOST_FOREACH (
                        const MaterialGraph::NamedMap::value_type& val,
                        material->getNodes()) {
                    fStats.accumulateMaterialNode(val.second);
                }
            }
        }
        void print(
MStringArray& result, 
bool printInstancedInfo)
 const 
        {
            fStats.print(result, printInstancedInfo);
        }
    private:
        virtual void visit(const XformData&   ,
                           const SubNode&     subNode)
        {
            
            BOOST_FOREACH(const SubNode::Ptr& child,
                          subNode.getChildren() ) {
                child->accept(*this);
            }
        }
        virtual void visit(const ShapeData&   shape,
                           const SubNode&     )
        {
            if (fAtGivenTime) {
                fStats.accumulate(shape, fTime);
            }
            else {
                fStats.accumulate(shape);
            }
        }
        const bool  fAtGivenTime;
        Stats fStats;
    };
    
    
    
    class DumpHierarchyVisitor : public SubNodeVisitor
    {
    public:
            : fResult(result),
              fLevel(0)
        {}
        virtual void visit(const XformData&   xform,
                           const SubNode&     subNode)
        {
            using namespace std;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "xform name = " << subNode.getName().asChar()
                    << ", tt = "  << subNode.transparentType()
                    << ", ptr = " << (void*)&subNode
                    << " {" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            ++fLevel;
            {
                std::string indent(kIndent*fLevel, ' ');
                BOOST_FOREACH(const XformData::SampleMap::value_type& sample,
                              xform.getSamples()) {
                    ostringstream tmp;
                    tmp << setw(kIndent*fLevel) << ' '
                        << "time = " << setw(10) << sample.first
                        << ", ptr = " << (void*)sample.second.get()
                        << ", vis = " << sample.second->visibility()
                        << ", bbox = ("
                        << setw(8) << sample.second->boundingBox().min().x << ","
                        << setw(8) << sample.second->boundingBox().min().y << ","
                        << setw(8) << sample.second->boundingBox().min().z << ") - ("
                        << setw(8) << sample.second->boundingBox().max().x << ","
                        << setw(8) << sample.second->boundingBox().max().y << ","
                        << setw(8) << sample.second->boundingBox().max().z << ")"
                        << ends;
                    fResult.append(
MString(tmp.str().c_str()));
                }
                
                BOOST_FOREACH(const SubNode::Ptr& child,
                              subNode.getChildren() ) {
                    child->accept(*this);
                }
            }
            --fLevel;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "}" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
        }
        virtual void visit(const ShapeData&   shape,
                           const SubNode&     subNode)
        {
            using namespace std;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "shape name = " << subNode.getName().asChar()
                    << ", tt = "  << subNode.transparentType()
                    << ", ptr = " << (void*)&subNode
                    << " {" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            ++fLevel;
            {
                std::string indent(kIndent*fLevel, ' ');
                BOOST_FOREACH(const ShapeData::SampleMap::value_type& sample,
                              shape.getSamples()) {
                    {
                        ostringstream tmp;
                        tmp << setw(kIndent*fLevel) << ' '
                            << "time = " << setw(10) << sample.first
                            << ", ptr = " << (void*)sample.second.get()
                            << ", vis = " << sample.second->visibility()
                            << ", nT = " << sample.second->numTriangles()
                            << ", nW = " << sample.second->numWires()
                            << ", nV = " << sample.second->numVerts()
                            << "," << ends;
                        fResult.append(
MString(tmp.str().c_str()));
                    }
                    {
                        ostringstream tmp;
                        tmp << setw(kIndent*fLevel) << ' '
                            << "P = " << (void*)sample.second->positions().get()
                            << ", N = " << (void*)sample.second->normals().get()
                            << "," << ends;
                        fResult.append(
MString(tmp.str().c_str()));
                    }
                    {
                        ostringstream tmp;
                        tmp << setw(kIndent*fLevel) << ' '
                            << "C = ("
                            << setw(8) << sample.second->diffuseColor()[0] << ","
                            << setw(8) << sample.second->diffuseColor()[1] << ","
                            << setw(8) << sample.second->diffuseColor()[2] << ","
                            << setw(8) << sample.second->diffuseColor()[3] << ","
                            << "), bbox = ("
                            << setw(8) << sample.second->boundingBox().min().x << ","
                            << setw(8) << sample.second->boundingBox().min().y << ","
                            << setw(8) << sample.second->boundingBox().min().z << ") - ("
                            << setw(8) << sample.second->boundingBox().max().x << ","
                            << setw(8) << sample.second->boundingBox().max().y << ","
                            << setw(8) << sample.second->boundingBox().max().z << ")"
                            << ends;
                        fResult.append(
MString(tmp.str().c_str()));
                    }
                    {
                        ostringstream tmp;
                        tmp << setw(kIndent*fLevel) << ' '
                            << "bbox place holder = " << (sample.second->isBoundingBoxPlaceHolder() ? "yes" : "no")
                            << ends;
                        fResult.append(
MString(tmp.str().c_str()));
                    }
                }
            }
            if (!shape.getMaterials().empty()) {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "materials = ";
                BOOST_FOREACH (
const MString& material, shape.getMaterials()) {
                    tmp << material.
asChar() << 
' ';
                }
                tmp << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            --fLevel;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "}" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
        }
    private:
        static const int kIndent = 2;
        int           fLevel;
    };
    
    
    
    class DumpMaterialVisitor
    {
    public:
            : fResult(result),
              fLevel(0)
        {}
        void dumpMaterials(const MaterialGraphMap::Ptr& materials)
        {
            using namespace std;
            BOOST_FOREACH (const MaterialGraphMap::NamedMap::value_type& val, materials->getGraphs())
            {
                const MaterialGraph::Ptr& graph = val.second;
                {
                    ostringstream tmp;
                    tmp << setw(kIndent*fLevel) << ' '
                        << "material graph name = " << graph->name().asChar()
                        << ", nNodes = " << graph->getNodes().size()
                        << ", ptr = " << (void*)graph.get()
                        << " {" << ends;
                    fResult.append(
MString(tmp.str().c_str()));
                }
                ++fLevel;
                BOOST_FOREACH (const MaterialGraph::NamedMap::value_type& val, graph->getNodes())
                {
                    dumpMaterialNode(val.second);
                }
                --fLevel;
                {
                    ostringstream tmp;
                    tmp << setw(kIndent*fLevel) << ' '
                        << "}" << ends;
                    fResult.append(
MString(tmp.str().c_str()));
                }
            }
        }
        void dumpMaterialNode(const MaterialNode::Ptr& node)
        {
            using namespace std;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "material node name = " << node->name().asChar()
                    << ", type = " << node->type()
                    << ", ptr = " << (void*)node.get()
                    << " {" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            ++fLevel;
            BOOST_FOREACH (const MaterialNode::PropertyMap::value_type& val, node->properties())
            {
                dumpMaterialProperty(val.second);
            }
            --fLevel;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "}" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
        }
        void dumpMaterialProperty(const MaterialProperty::Ptr& prop)
        {
            using namespace std;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "prop name = " << prop->name().asChar()
                    << ", type = " << propertyTypeString(prop)
                    << ", ptr = " << (void*)prop.get()
                    << " {" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            ++fLevel;
            BOOST_FOREACH (const MaterialProperty::SampleMap::value_type& val, prop->getSamples())
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "time = " << setw(10) << val.first
                    << ", value = " << propertyValueString(val.first, prop)
                    << ", ptr = " << (void*)val.second.get()
                    << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            const MaterialNode::Ptr     srcNode = prop->srcNode();
            const MaterialProperty::Ptr srcProp = prop->srcProp();
            if (srcNode && srcProp) {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "src node = " << srcNode->name().asChar()
                    << ", src prop = " << srcProp->name().asChar()
                    << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
            --fLevel;
            {
                ostringstream tmp;
                tmp << setw(kIndent*fLevel) << ' '
                    << "}" << ends;
                fResult.append(
MString(tmp.str().c_str()));
            }
        }
        std::string propertyTypeString(const MaterialProperty::Ptr& prop)
        {
            switch (prop->type()) {
            case MaterialProperty::kBool:   return "bool";
            case MaterialProperty::kInt32:  return "int32";
            case MaterialProperty::kFloat:  return "float";
            case MaterialProperty::kFloat2: return "float2";
            case MaterialProperty::kFloat3: return "float3";
            case MaterialProperty::kRGB:    return "rgb";
            case MaterialProperty::kString: return "string";
            default:             assert(0); return "unknown";
            }
        }
        std::string propertyValueString(double seconds, const MaterialProperty::Ptr& prop)
        {
            std::ostringstream tmp;
            switch (prop->type()) {
            case MaterialProperty::kBool:
                tmp << (prop->asBool(seconds) ? "true" : "false");
                break;
            case MaterialProperty::kInt32:
                tmp << prop->asInt32(seconds);
                break;
            case MaterialProperty::kFloat:
                tmp << prop->asFloat(seconds);
                break;
            case MaterialProperty::kFloat2:
                {
                    float x, y;
                    prop->asFloat2(seconds, x, y);
                    tmp << "(" << x << "," << y << ")";
                    break;
                }
            case MaterialProperty::kFloat3:
                {
                    float x, y, z;
                    prop->asFloat3(seconds, x, y, z);
                    tmp << "(" << x << "," << y << "," << z << ")";
                    break;
                }
            case MaterialProperty::kRGB:
                {
                    MColor c = prop->asColor(seconds);
 
                    tmp << 
"rgb(" << c.
r << 
"," << c.
g << 
"," << c.
b << 
")";
                    break;
                }
            case MaterialProperty::kString:
                tmp << prop->asString(seconds).asChar();
                break;
            default:
                assert(0);
                tmp << "unknown type";
                break;
            }
            return tmp.str();
        }
    private:
        static const int kIndent = 2;
        int           fLevel;
    };
    
    
    
    class ProgressBar
    {
    public:
        {
            
            reset(msg, max);
        }
        {
        }
        ~ProgressBar()
        {
            endProgress();
        }
        void stepProgress() const
        {
            if (fShowProgress) {
            }
        }
        bool isCancelled() const
        {
            int isCancelled = 0;
            if (fShowProgress) {
            }
            if (isCancelled) {
                return true;
            }
            return false;
        }
    private:
        
        ProgressBar(const ProgressBar&);
        const ProgressBar& operator=(const ProgressBar&);
        ProgressBar(
const MString& msg, 
unsigned int max)
        {
            beginProgress(msg, max);
        }
        void beginProgress(
const MString& msg, 
unsigned int max)
 const 
        {
            if (fShowProgress) {
                
                if (max <= 0) {
                    max = 1;
                }
                maxValue += max;
                
                
                progressBarCmd.
format(
"progressBar -e -bp -ii 1 -st \"^1s\" -max ^2s $gMainProgressBar",
                    msg, maxValue);
            }
        }
        void endProgress() const
        {
            if (fShowProgress) {
            }
        }
        bool fShowProgress;  
    };
    
    
    
    class GroupCreator
    {
    public:
        GroupCreator() {}
        ~GroupCreator() {}
        void addChild(const SubNode::MPtr& childNode)
        {
            XformData::Ptr childXform = boost::dynamic_pointer_cast<const XformData>(
                    childNode->getData());
            assert(childXform);
            if (childXform) {
                fChildNodes.push_back(childNode);
                fChildXforms.push_back(childXform);
            }
        }
        void group()
        {
            assert(!fGroup);
            fGroup = XformData::create();
            
            std::set<double> times;
            BOOST_FOREACH(const XformData::Ptr child, fChildXforms) {
                BOOST_FOREACH(const XformData::SampleMap::value_type& val, child->getSamples()) {
                    times.insert(val.first);
                }
            }
            std::set<double>::const_iterator timeIt  = times.begin();
            std::set<double>::const_iterator timeEnd = times.end();
            if (timeIt != timeEnd) {
                fGroup->addSample(XformSample::create(
                    *timeIt,
                    true));
            }
        }
        SubNode::MPtr getSubNode(
const MString& name)
        {
            SubNode::MPtr subNode = SubNode::create(name, fGroup);
            BOOST_FOREACH(const SubNode::MPtr& childNode, fChildNodes) {
                SubNode::connect(subNode, childNode);
            }
            return subNode;
        }
    private:
        
        GroupCreator(const GroupCreator&);
        const GroupCreator& operator= (const GroupCreator&);
        std::vector<SubNode::MPtr>   fChildNodes;
        std::vector<XformData::Ptr>  fChildXforms;
        XformData::MPtr              fGroup;
    };
    
    
    
    class XformFreezer : public SubNodeVisitor
    {
    public:
        typedef std::vector<ShapeData::Ptr>                            FrozenGeometries;
        typedef std::vector<std::pair<XformData::Ptr,ShapeData::Ptr> > AnimatedGeometries;
        typedef std::set<double>                                       TimeSet;
        XformFreezer(const XformData::Ptr& parentXform,
                     FrozenGeometries&     frozenGeometries,
                     bool                  dontFreezeAnimatedObjects,
                     AnimatedGeometries&   animatedGeometries)
            : fParentXform(parentXform),
              fFrozenGeometries(frozenGeometries),
              fDontFreezeAnimatedObjects(dontFreezeAnimatedObjects),
              fAnimatedGeometries(animatedGeometries)
        {}
        virtual void visit(const XformData& xform,
                           const SubNode&   subNode)
        {
            
            TimeSet times;
            BOOST_FOREACH(const XformData::SampleMap::value_type& val,
                    fParentXform->getSamples() ) {
                times.insert(val.first);
            }
            BOOST_FOREACH(const XformData::SampleMap::value_type& val,
                    xform.getSamples()) {
                times.insert(val.first);
            }
            
            XformData::MPtr frozenXform = XformData::create();
            BOOST_FOREACH(const double& time, times) {
                
                boost::shared_ptr<const XformSample> parentSample =
                        fParentXform->getSample(time);
                
                boost::shared_ptr<const XformSample> sample =
                        xform.getSample(time);
                frozenXform->addSample(XformSample::create(
                    time,
                    sample->xform() * parentSample->xform(),
                    sample->visibility() && parentSample->visibility()));
            }
            
            BOOST_FOREACH(const SubNode::Ptr& child, subNode.getChildren()) {
                XformFreezer xformFreezer(frozenXform, fFrozenGeometries,
                    fDontFreezeAnimatedObjects, fAnimatedGeometries);
                child->accept(xformFreezer);
            }
        }
        virtual void visit(const ShapeData& shape,
                           const SubNode&   subNode)
        {
            
            if (fDontFreezeAnimatedObjects) {
                
                
                
                if (fParentXform->getSamples().size() > 1 && shape.getSamples().size() <= 1) {
                    
                    XformData::MPtr animatedXform = XformData::create();
                    BOOST_FOREACH (const XformData::SampleMap::value_type& val,
                            fParentXform->getSamples()) {
                        animatedXform->addSample(val.second);
                    }
                    
                    ShapeData::MPtr animatedShape = ShapeData::create();
                    BOOST_FOREACH (const ShapeData::SampleMap::value_type& val,
                            shape.getSamples()) {
                        animatedShape->addSample(val.second);
                    }
                    animatedShape->setMaterials(shape.getMaterials());
                    
                    
                    fAnimatedGeometries.push_back(
                        std::make_pair(animatedXform, animatedShape));
                    return;
                }
            }
            
            TimeSet times;
            BOOST_FOREACH(const XformData::SampleMap::value_type& val,
                    fParentXform->getSamples()) {
                times.insert(val.first);
            }
            BOOST_FOREACH(const ShapeData::SampleMap::value_type& val,
                    shape.getSamples()) {
                times.insert(val.first);
            }
            
            ShapeData::MPtr frozenShape = ShapeData::create();
            TimeSet::const_iterator it  = times.begin();
            TimeSet::const_iterator end = times.end();
            if (it != end) {
                
                boost::shared_ptr<const XformSample> xformSample =
                        fParentXform->getSample(*it);
                boost::shared_ptr<const ShapeSample> shapeSample =
                        shape.getSample(*it);
                
                boost::shared_ptr<const ShapeSample> frozenSample;
                if (xformSample->visibility() && shapeSample->visibility()) {
                    frozenSample = freezeSample(*it, xformSample, shapeSample);
                }
                else {
                    frozenSample = ShapeSample::createEmptySample(*it);
                }
                
                frozenShape->addSample(frozenSample);
                ++it;
                for (; it != end; ++it) {
                    
                    boost::shared_ptr<const XformSample> prevXformSample = xformSample;
                    boost::shared_ptr<const ShapeSample> prevShapeSample = shapeSample;
                    
                    xformSample = fParentXform->getSample(*it);
                    shapeSample = shape.getSample(*it);
                    if (xformSample->visibility() && shapeSample->visibility()) {
                        if (!xformSample->xform().isEquivalent(prevXformSample->xform()) ||
                            xformSample->visibility() != prevXformSample->visibility() ||
                            shapeSample->wireVertIndices() != prevShapeSample->wireVertIndices() ||
                            shapeSample->triangleVertexIndexGroups() != prevShapeSample->triangleVertexIndexGroups() ||
                            shapeSample->positions() != prevShapeSample->positions() ||
                            shapeSample->normals()   != prevShapeSample->normals() ||
                            shapeSample->diffuseColor() != prevShapeSample->diffuseColor() ||
                            shapeSample->visibility() != prevShapeSample->visibility())
                        {
                            
                            frozenSample = freezeSample(*it, xformSample, shapeSample);
                        }
                        else {
                            
                            boost::shared_ptr<ShapeSample> newFrozenSample =
                                ShapeSample::create(
                                *it,
                                shapeSample->numWires(),
                                shapeSample->numVerts(),
                                shapeSample->wireVertIndices(),
                                shapeSample->triangleVertexIndexGroups(),
                                frozenSample->positions(),
                                frozenSample->boundingBox(),
                                shapeSample->diffuseColor(),
                                xformSample->visibility() && shapeSample->visibility());
                            newFrozenSample->setNormals(frozenSample->normals());
                            newFrozenSample->setUVs(shapeSample->uvs());
                            frozenSample = newFrozenSample;
                        }
                    }
                    else {
                        frozenSample = ShapeSample::createEmptySample(*it);
                    }
                    
                    frozenShape->addSample(frozenSample);
                }
            }
            frozenShape->setMaterials(shape.getMaterials());
            fFrozenGeometries.push_back(frozenShape);
        }
    private:
        boost::shared_ptr<const ShapeSample> freezeSample(
            const double time,
            const boost::shared_ptr<const XformSample>& xform,
            const boost::shared_ptr<const ShapeSample>& shape)
        {
            const size_t numWires = shape->numWires();
            const size_t numVerts = shape->numVerts();
            boost::shared_ptr<IndexBuffer> wireVertIndices =
                    shape->wireVertIndices();
            std::vector<boost::shared_ptr<IndexBuffer> > triangleVertexIndexGroups =
                    shape->triangleVertexIndexGroups();
            boost::shared_ptr<VertexBuffer> uvs =
                    shape->uvs();
            MColor diffuseColor = shape->diffuseColor();
 
            bool visibility = shape->visibility() && xform->visibility();
            
            if (numWires == 0 || numVerts == 0 || !wireVertIndices || 
                    triangleVertexIndexGroups.empty()) {
                return ShapeSample::createEmptySample(time);
            }
            boost::shared_ptr<VertexBuffer> positions;
            boost::shared_ptr<VertexBuffer> normals;
            MMatrix xformMatrix = xform->xform();
 
                
                positions    = shape->positions();
                normals      = shape->normals();
                boundingBox  = shape->boundingBox();
            }
            else {
                float xform[4][4];
                float xformIT[4][4];
                const bool isReflection = xformMatrix.
det3x3() < 0.0;
 
                if (isReflection) {
                    
                    
                    
                    std::vector<boost::shared_ptr<IndexBuffer> > newTriangleVertexIndexGroups;
                    BOOST_FOREACH(const boost::shared_ptr<IndexBuffer>& srcIdxBuf,
                                  triangleVertexIndexGroups) {
                        typedef IndexBuffer::index_t index_t;
                        const size_t numIndices = srcIdxBuf->numIndices();
                        IndexBuffer::ReadInterfacePtr readable = srcIdxBuf->readableInterface();
                        const index_t* srcIndices = readable->get();
                        const boost::shared_array<IndexBuffer::index_t> dstIndices(
                            new index_t[numIndices]);
                        for (size_t i=0; i<numIndices; i+=3) {
                            dstIndices[i + 0] = srcIndices[i + 2];
                            dstIndices[i + 1] = srcIndices[i + 1];
                            dstIndices[i + 2] = srcIndices[i + 0];
                        }
                        boost::shared_ptr<IndexBuffer> dstIdxBuf(
                            IndexBuffer::create(
                                SharedArray<index_t>::create(dstIndices, numIndices)));
                        newTriangleVertexIndexGroups.push_back(dstIdxBuf);
                    }
                    triangleVertexIndexGroups.swap(newTriangleVertexIndexGroups);
                }
                
                VertexBuffer::ReadInterfacePtr srcPosRead = shape->positions()->readableInterface();
                const float* srcPositions = srcPosRead->get();
                VertexBuffer::ReadInterfacePtr srcNormRead = shape->normals()->readableInterface();
                const float* srcNormals   = srcNormRead->get();
                boost::shared_array<float> dstPositions(new float[3 * numVerts]);
                boost::shared_array<float> dstNormals(new float[3 * numVerts]);
                float minX = +std::numeric_limits<float>::max();
                float minY = +std::numeric_limits<float>::max();
                float minZ = +std::numeric_limits<float>::max();
                float maxX = -std::numeric_limits<float>::max();
                float maxY = -std::numeric_limits<float>::max();
                float maxZ = -std::numeric_limits<float>::max();
                for (size_t i=0; i<numVerts; ++i) {
                    const float x = srcPositions[3*i + 0];
                    const float y = srcPositions[3*i + 1];
                    const float z = srcPositions[3*i + 2];
                    const float xp =
                        xform[0][0] * x + xform[1][0] * y + xform[2][0] * z + xform[3][0];
                    const float yp =
                        xform[0][1] * x + xform[1][1] * y + xform[2][1] * z + xform[3][1];
                    const float zp =
                        xform[0][2] * x + xform[1][2] * y + xform[2][2] * z + xform[3][2];
                    minX = std::min(xp, minX);
                    minY = std::min(yp, minY);
                    minZ = std::min(zp, minZ);
                    maxX = std::max(xp, maxX);
                    maxY = std::max(yp, maxY);
                    maxZ = std::max(zp, maxZ);
                    dstPositions[3*i + 0] = xp;
                    dstPositions[3*i + 1] = yp;
                    dstPositions[3*i + 2] = zp;
                    const float nx = srcNormals[3*i + 0];
                    const float ny = srcNormals[3*i + 1];
                    const float nz = srcNormals[3*i + 2];
                    dstNormals[3*i + 0] =
                        xformIT[0][0] * nx + xformIT[1][0] * ny + xformIT[2][0] * nz + xformIT[3][0];
                    dstNormals[3*i + 1] =
                        xformIT[0][1] * nx + xformIT[1][1] * ny + xformIT[2][1] * nz + xformIT[3][1];
                    dstNormals[3*i + 2] =
                        xformIT[0][2] * nx + xformIT[1][2] * ny + xformIT[2][2] * nz + xformIT[3][2];
                }
                positions = VertexBuffer::createPositions(
                    SharedArray<float>::create(dstPositions, 3 * numVerts));
                normals   = VertexBuffer::createNormals(
                    SharedArray<float>::create(dstNormals, 3 * numVerts));
            }
            boost::shared_ptr<ShapeSample> frozenSample =
                ShapeSample::create(
                    time,
                    numWires,
                    numVerts,
                    wireVertIndices,
                    triangleVertexIndexGroups,
                    positions,
                    boundingBox,
                    diffuseColor,
                    visibility);
            frozenSample->setNormals(normals);
            frozenSample->setUVs(uvs);
            return frozenSample;
        }
        
        XformFreezer(const XformFreezer&);
        const XformFreezer& operator= (const XformFreezer&);
        XformData::Ptr      fParentXform;
        FrozenGeometries&   fFrozenGeometries;
        AnimatedGeometries& fAnimatedGeometries;
        bool                fDontFreezeAnimatedObjects;
    };
    
    
    
    class ConsolidateBuckets
    {
    public:
        struct BucketKey
        {
            typedef std::map<double,MColor> DiffuseColorMap;
            typedef std::map<double,bool>   VisibilityMap;
            typedef std::map<double,size_t> IndexGroupMap;
            typedef std::vector<MString>    MaterialsAssignment;
            BucketKey(const ShapeData::Ptr& shape)
            {
                ShapeData::SampleMap::const_iterator it  = shape->getSamples().begin();
                ShapeData::SampleMap::const_iterator end = shape->getSamples().end();
                if (it != end) {
                    MColor diffuseColor = (*it).second->diffuseColor();
 
                    bool   visibility   = (*it).second->visibility();
                    size_t indexGroups  = (*it).second->numIndexGroups();
                    fDiffuseColor[(*it).first] = diffuseColor;
                    fVisibility[(*it).first]    = visibility;
                    fIndexGroup[(*it).first]   = indexGroups;
                    ++it;
                    for (; it != end; ++it) {
                        MColor prevDiffuseColor = diffuseColor;
 
                        bool   prevVisibility   = visibility;
                        size_t prevIndexGroups  = indexGroups;
                        MColor diffuseColor = (*it).second->diffuseColor();
 
                        bool   visibility   = (*it).second->visibility();
                        size_t indexGroups  = (*it).second->numIndexGroups();
                        if (prevDiffuseColor != diffuseColor) {
                            fDiffuseColor[(*it).first] = diffuseColor;
                        }
                        if (prevVisibility != visibility) {
                            fVisibility[(*it).first]    = visibility;
                        }
                        if (prevIndexGroups != indexGroups) {
                            fIndexGroup[(*it).first]   = indexGroups;
                        }
                    }
                }
                fMaterials = shape->getMaterials();
            }
            struct Hash : std::unary_function<BucketKey, std::size_t>
            {
                std::size_t operator()(const BucketKey& key) const
                {
                    std::size_t seed = 0;
                    BOOST_FOREACH(const DiffuseColorMap::value_type& val, key.fDiffuseColor) {
                        boost::hash_combine(seed, val.first);
                        boost::hash_combine(seed, val.second.r);
                        boost::hash_combine(seed, val.second.g);
                        boost::hash_combine(seed, val.second.b);
                        boost::hash_combine(seed, val.second.a);
                    }
                    BOOST_FOREACH(const VisibilityMap::value_type& val, key.fVisibility) {
                        boost::hash_combine(seed, val.first);
                        boost::hash_combine(seed, val.second);
                    }
                    BOOST_FOREACH(const IndexGroupMap::value_type& val, key.fIndexGroup) {
                        boost::hash_combine(seed, val.first);
                        boost::hash_combine(seed, val.second);
                    }
                    BOOST_FOREACH(
const MString& material, key.fMaterials) {
                        unsigned int length = material.
length();
 
                        const char* begin = material.
asChar();
 
                        size_t hash = boost::hash_range(begin, begin + length);
                        boost::hash_combine(seed, hash);
                    }
                    return seed;
                }
            };
            struct EqualTo : std::binary_function<BucketKey, BucketKey, bool>
            {
                bool operator()(const BucketKey& x, const BucketKey& y) const
                {
                    return x.fDiffuseColor == y.fDiffuseColor &&
                            x.fVisibility == y.fVisibility &&
                            x.fIndexGroup == y.fIndexGroup &&
                            x.fMaterials == y.fMaterials;
                }
            };
            DiffuseColorMap     fDiffuseColor;
            VisibilityMap       fVisibility;
            IndexGroupMap       fIndexGroup;
            MaterialsAssignment fMaterials;
        };
        typedef std::multimap<size_t,ShapeData::Ptr>  Bucket;
        typedef boost::unordered_map<BucketKey,Bucket,BucketKey::Hash,BucketKey::EqualTo> BucketMap;
        typedef std::list<Bucket> BucketList;
        ConsolidateBuckets(const XformFreezer::FrozenGeometries& shapes)
            : fShapes(shapes)
        {}
        void divide()
        {
            BOOST_FOREACH(const ShapeData::Ptr& shape , fShapes) {
                BucketKey key(shape);
                std::pair<BucketMap::iterator,bool> ret =
                        fBucketMap.insert(std::make_pair(key, Bucket()));
                ret.first->second.insert(std::make_pair(maxNumVerts(shape), shape));
            }
        }
        void getBucketList(BucketList& bucketList)
        {
            bucketList.clear();
            BOOST_FOREACH(const BucketMap::value_type& val, fBucketMap) {
                bucketList.push_back(val.second);
            }
        }
    private:
        
        ConsolidateBuckets(const ConsolidateBuckets&);
        const ConsolidateBuckets& operator= (const ConsolidateBuckets&);
        const XformFreezer::FrozenGeometries& fShapes;
        BucketMap                             fBucketMap;
    };
    
    
    
    class FirstSampleTime : public SubNodeVisitor
    {
    public:
        FirstSampleTime()
            : fTime(0)
        {}
        virtual void visit(const XformData& xform,
                           const SubNode&   subNode)
        {
            fTime = xform.getSamples().begin()->first;
        }
        virtual void visit(const ShapeData& shape,
                           const SubNode&   subNode)
        {
            fTime = shape.getSamples().begin()->first;
        }
        double get()
        { return fTime; }
    private:
        
        FirstSampleTime(const FirstSampleTime&);
        const FirstSampleTime& operator= (const FirstSampleTime&);
        double fTime;
    };
    
    
    
    class Consolidator
    {
    public:
        Consolidator(SubNode::MPtr rootNode, const int threshold, const bool motionBlur)
            : fRootNode(rootNode), fThreshold(threshold), fMotionBlur(motionBlur)
        {}
        ~Consolidator()
        {}
        {
            
            
            
            
            
            
            
            
            
            
            double firstSampleTime = 0;
            {
                FirstSampleTime firstSampleTimeVisitor;
                fRootNode->accept(firstSampleTimeVisitor);
                firstSampleTime = firstSampleTimeVisitor.get();
            }
            
            XformFreezer::FrozenGeometries   frozenGeometries;
            XformFreezer::AnimatedGeometries animatedGeometries;
            {
                
                XformData::MPtr identityXformData = XformData::create();
                identityXformData->addSample(XformSample::create(
                    firstSampleTime,
                    true));
                
                XformFreezer xformFreezer(identityXformData, frozenGeometries,
                    fMotionBlur, animatedGeometries);
                fRootNode->accept(xformFreezer);
            }
            
            ConsolidateBuckets::BucketList bucketList;
            {
                ConsolidateBuckets buckets(frozenGeometries);
                buckets.divide();
                buckets.getBucketList(bucketList);
            }
            
            ProgressBar progressBar(kOptimizingMsg, (unsigned int)frozenGeometries.size());
            
            std::vector<ShapeData::Ptr> newShapes;
            std::vector<ShapeData::Ptr> consolidatedShapes;
            BOOST_FOREACH(ConsolidateBuckets::Bucket& bucket, bucketList) {
                
                while (!bucket.empty()) {
                    const ConsolidateBuckets::Bucket::iterator largestNode = --bucket.end();
                    MInt64 numRemainingVerts = fThreshold - largestNode->first;
                    if (numRemainingVerts < 0) {
                        
                        newShapes.push_back(largestNode->second);
                        bucket.erase(largestNode);
                        MUpdateProgressAndCheckInterruption(progressBar);
                    }
                    else {
                        
                        consolidatedShapes.push_back(largestNode->second);
                        bucket.erase(largestNode);
                        MUpdateProgressAndCheckInterruption(progressBar);
                        while (numRemainingVerts > 0 && !bucket.empty()) {
                            ConsolidateBuckets::Bucket::iterator node =
                                    bucket.upper_bound((size_t)numRemainingVerts);
                            if (node == bucket.begin()) break;
                            --node;
                            numRemainingVerts -= (MInt64)node->first;
                            consolidatedShapes.push_back(node->second);
                            bucket.erase(node);
                            MUpdateProgressAndCheckInterruption(progressBar);
                        }
                        
                        consolidateGeometry(newShapes, consolidatedShapes);
                    }
                }
            }
            
            std::vector<XformData::Ptr> newXforms;
            BOOST_FOREACH(const ShapeData::Ptr& newShape, newShapes) {
                XformData::MPtr newXform = XformData::create();
                ShapeData::SampleMap::const_iterator it  = newShape->getSamples().begin();
                ShapeData::SampleMap::const_iterator end = newShape->getSamples().end();
                if (it != end) {
                    newXform->addSample(XformSample::create(
                        (*it).first,
                        true));
                }
                newXforms.push_back(newXform);
            }
            
            std::vector<std::pair<XformData::Ptr,ShapeData::Ptr> > finalXformsAndShapes;
            for (size_t i = 0; i < newXforms.size(); i++) {
                finalXformsAndShapes.push_back(std::make_pair(newXforms[i], newShapes[i]));
            }
            finalXformsAndShapes.insert(finalXformsAndShapes.end(),
                animatedGeometries.begin(), animatedGeometries.end());
            
            if (finalXformsAndShapes.size() == 1) {
                
                SubNode::MPtr xformNode = SubNode::create(
                    fRootNode->getName(),
                    finalXformsAndShapes[0].first);
                SubNode::MPtr shapeNode = SubNode::create(
                    fRootNode->getName() + "Shape",
                    finalXformsAndShapes[0].second);
                SubNode::connect(xformNode, shapeNode);
                fConsolidatedRootNode = xformNode;
            }
            else if (finalXformsAndShapes.size() > 1) {
                
                
                XformData::MPtr topXform = XformData::create();
                std::set<double> times;
                for (size_t i = 0; i < finalXformsAndShapes.size(); i++) {
                    BOOST_FOREACH(const XformData::SampleMap::value_type& val,
                            finalXformsAndShapes[i].first->getSamples()) {
                        times.insert(val.first);
                    }
                    BOOST_FOREACH(const ShapeData::SampleMap::value_type& val,
                            finalXformsAndShapes[i].second->getSamples()) {
                        times.insert(val.first);
                    }
                }
                std::set<double>::const_iterator timeIt  = times.begin();
                std::set<double>::const_iterator timeEnd = times.end();
                if (timeIt != timeEnd) {
                    topXform->addSample(XformSample::create(
                        *timeIt,
                        true));
                }
                SubNode::MPtr topXformNode = SubNode::create(
                    fRootNode->getName(),
                    topXform);
                
                
                for (size_t i = 0; i < finalXformsAndShapes.size(); i++) {
                    SubNode::MPtr xformNode = SubNode::create(
                        fRootNode->getName() + (i + 1),
                        finalXformsAndShapes[i].first);
                    SubNode::MPtr shapeNode = SubNode::create(
                        fRootNode->getName() + "Shape" + (i + 1),
                        finalXformsAndShapes[i].second);
                    SubNode::connect(xformNode, shapeNode);
                    SubNode::connect(topXformNode, xformNode);
                }
                fConsolidatedRootNode = topXformNode;
            }
        }
        SubNode::MPtr consolidatedRootNode()
        { return fConsolidatedRootNode; }
    private:
        
        Consolidator(const Consolidator&);
        const Consolidator& operator= (const Consolidator&);
        void consolidateGeometry(std::vector<ShapeData::Ptr>& newShapes,
                                 std::vector<ShapeData::Ptr>& consolidatedShapes)
        {
            
            std::set<double> times;
            BOOST_FOREACH(const ShapeData::Ptr& shape, consolidatedShapes) {
                BOOST_FOREACH(const ShapeData::SampleMap::value_type& smv,
                              shape->getSamples()) {
                    times.insert(smv.first);
                }
            }
            
            ShapeData::MPtr newShape = ShapeData::create();
            const size_t nbShapes = consolidatedShapes.size();
            std::set<double>::const_iterator timeIt  = times.begin();
            std::set<double>::const_iterator timeEnd = times.end();
            
            typedef IndexBuffer::index_t index_t;
            boost::shared_array<index_t>               wireVertIndices;
            std::vector<boost::shared_array<index_t> > triangleVertIndices;
            boost::shared_array<float>   positions;
            boost::shared_array<float>   normals;
            boost::shared_array<float>   uvs;
            bool   visibility = true;
            {
                size_t totalWires = 0;
                size_t totalVerts = 0;
                std::vector<size_t> totalTriangles;
                size_t numIndexGroups = 0;
                bool uvExists     = false;
                for (size_t i = 0; i < nbShapes; i++) {
                    ShapeData::Ptr& shape = consolidatedShapes[i];
                    const boost::shared_ptr<const ShapeSample>& sample =
                        shape->getSample(*timeIt);
                    totalWires += sample->numWires();
                    totalVerts += sample->numVerts();
                    if (numIndexGroups == 0) {
                        
                        
                        numIndexGroups = sample->numIndexGroups();
                        totalTriangles.resize(numIndexGroups, 0);
                        diffuseColor = sample->diffuseColor();
                        visibility   = sample->visibility();
                    }
                    
                    
                    assert(numIndexGroups == sample->numIndexGroups());
                    assert(fabs(diffuseColor.
r - sample->diffuseColor().r) < 1e-5);
                    assert(fabs(diffuseColor.
g - sample->diffuseColor().g) < 1e-5);
                    assert(fabs(diffuseColor.
b - sample->diffuseColor().b) < 1e-5);
                    assert(fabs(diffuseColor.
a - sample->diffuseColor().a) < 1e-5);
                    assert(visibility == sample->visibility());
                    for (size_t j = 0; j < totalTriangles.size(); j++) {
                        totalTriangles[j] += sample->numTriangles(j);
                    }
                    
                    if (!uvExists && sample->uvs()) {
                        uvExists = true;
                    }
                }
                wireVertIndices = boost::shared_array<index_t>(
                    new index_t[2 * totalWires]);
                triangleVertIndices.resize(totalTriangles.size());
                for (size_t i = 0; i < totalTriangles.size(); i++) {
                    triangleVertIndices[i] = boost::shared_array<index_t>(
                        new index_t[3 * totalTriangles[i]]);
                }
                positions = boost::shared_array<float>(
                    new float[3 * totalVerts]);
                normals = boost::shared_array<float>(
                    new float[3 * totalVerts]);
                if (uvExists) {
                    uvs = boost::shared_array<float>(
                        new float[2 * totalVerts]);
                }
                {
                    size_t wireIdx = 0;
                    size_t vertIdx = 0;
                    std::vector<size_t> triangleIdx(numIndexGroups, 0);
                    for (size_t i = 0; i < nbShapes; i++) {
                        const boost::shared_ptr<const ShapeSample>& sample =
                            consolidatedShapes[i]->getSample(*timeIt);
                        const size_t numWires     = sample->numWires();
                        const size_t numVerts     = sample->numVerts();
                        
                        if (sample->wireVertIndices()) {
                            IndexBuffer::ReadInterfacePtr readable = sample->wireVertIndices()->readableInterface();
                            const index_t* srcWireVertIndices = readable->get();
                            for (size_t j = 0; j < numWires; j++) {
                                wireVertIndices[2*(j + wireIdx) + 0] = index_t(srcWireVertIndices[2*j + 0] + vertIdx);
                                wireVertIndices[2*(j + wireIdx) + 1] = index_t(srcWireVertIndices[2*j + 1] + vertIdx);
                            }
                        }
                        
                        for (size_t group = 0; group < numIndexGroups; group++) {
                            const size_t numTriangles = sample->numTriangles(group);
                            if (sample->triangleVertIndices(group)) {
                                IndexBuffer::ReadInterfacePtr readable = sample->triangleVertIndices(group)->readableInterface();
                                const index_t* srcTriangleVertIndices = readable->get();
                                for (size_t j = 0; j < numTriangles; j++) {
                                    triangleVertIndices[group][3*(j + triangleIdx[group]) + 0] = index_t(srcTriangleVertIndices[3*j + 0] + vertIdx);
                                    triangleVertIndices[group][3*(j + triangleIdx[group]) + 1] = index_t(srcTriangleVertIndices[3*j + 1] + vertIdx);
                                    triangleVertIndices[group][3*(j + triangleIdx[group]) + 2] = index_t(srcTriangleVertIndices[3*j + 2] + vertIdx);
                                }
                            }
                        }
                        
                        if (sample->positions()) {
                            VertexBuffer::ReadInterfacePtr readable = sample->positions()->readableInterface();
                            memcpy(&positions[3*vertIdx], readable->get(), 3*numVerts*sizeof(float));
                        }
                        
                        if (sample->normals()) {
                            VertexBuffer::ReadInterfacePtr readable = sample->normals()->readableInterface();
                            memcpy(&normals[3*vertIdx], readable->get(), 3*numVerts*sizeof(float));
                        }
                        
                        if (sample->uvs()) {
                            VertexBuffer::ReadInterfacePtr readable = sample->uvs()->readableInterface();
                            memcpy(&uvs[2*vertIdx], readable->get(), 2*numVerts*sizeof(float));
                        }
                        else if (uvExists) {
                            memset(&uvs[2*vertIdx], 0, 2*numVerts*sizeof(float));
                        }
                        wireIdx += numWires;
                        vertIdx += numVerts;
                        for (size_t i = 0; i < numIndexGroups; i++) {
                            triangleIdx[i] += sample->numTriangles(i);
                        }
                        boundingBox.
expand(sample->boundingBox());
                    }
                }
                std::vector<boost::shared_ptr<IndexBuffer> > newTriangleVertIndices(numIndexGroups);
                for (size_t i = 0; i < numIndexGroups; i++) {
                    newTriangleVertIndices[i] = IndexBuffer::create(
                        SharedArray<index_t>::create(
                            triangleVertIndices[i], 3 * totalTriangles[i]));
                }
                boost::shared_ptr<ShapeSample> newSample = ShapeSample::create(
                        *timeIt,
                        totalWires,
                        totalVerts,
                        IndexBuffer::create(
                            SharedArray<index_t>::create(
                                wireVertIndices, 2 * totalWires)),
                        newTriangleVertIndices,
                        VertexBuffer::createPositions(
                            SharedArray<float>::create(
                                positions, 3 * totalVerts)),
                        boundingBox,
                        diffuseColor,
                        visibility);
                if (normals) {
                    newSample->setNormals(
                        VertexBuffer::createNormals(
                            SharedArray<float>::create(
                                normals, 3 * totalVerts)));
                }
                if (uvs) {
                    newSample->setUVs(
                        VertexBuffer::createUVs(
                            SharedArray<float>::create(
                                uvs, 2 * totalVerts)));
                }
                newShape->addSample(newSample);
            }
            
            std::set<double>::const_iterator timePrev  = timeIt;
            ++timeIt;
            for (; timeIt != timeEnd; ++timeIt) {
                size_t totalWires     = 0;
                size_t totalVerts     = 0;
                std::vector<size_t> totalTriangles;
                size_t numIndexGroups = 0;
                bool uvExists       = false;
                bool wiresDirty     = false;
                bool trianglesDirty = false;
                bool positionsDirty = false;
                bool normalsDirty   = false;
                bool uvsDirty       = false;
                for (size_t i = 0; i < nbShapes; i++) {
                    ShapeData::Ptr& shape = consolidatedShapes[i];
                    const boost::shared_ptr<const ShapeSample>& sample =
                        shape->getSample(*timeIt);
                    const boost::shared_ptr<const ShapeSample>& prevSample =
                        shape->getSample(*timePrev);
                    totalWires += sample->numWires();
                    totalVerts += sample->numVerts();
                    if (numIndexGroups == 0) {
                        
                        
                        numIndexGroups = sample->numIndexGroups();
                        totalTriangles.resize(numIndexGroups, 0);
                        diffuseColor = sample->diffuseColor();
                        visibility   = sample->visibility();
                    }
                    
                    
                    assert(numIndexGroups == sample->numIndexGroups());
                    assert(fabs(diffuseColor.
r - sample->diffuseColor().r) < 1e-5);
                    assert(fabs(diffuseColor.
g - sample->diffuseColor().g) < 1e-5);
                    assert(fabs(diffuseColor.
b - sample->diffuseColor().b) < 1e-5);
                    assert(fabs(diffuseColor.
a - sample->diffuseColor().a) < 1e-5);
                    assert(visibility == sample->visibility());
                    for (size_t j = 0; j < totalTriangles.size(); j++) {
                        totalTriangles[j] += sample->numTriangles(j);
                    }
                    
                    if (!uvExists && sample->uvs()) {
                        uvExists = true;
                    }
                    for (size_t j = 0; j < numIndexGroups; j++) {
                        trianglesDirty |= sample->triangleVertIndices(j) != prevSample->triangleVertIndices(j);
                    }
                    wiresDirty     |= sample->wireVertIndices()     != prevSample->wireVertIndices();
                    positionsDirty |= sample->positions()           != prevSample->positions();
                    normalsDirty   |= sample->normals()             != prevSample->normals();
                    uvsDirty       |= sample->uvs()                 != prevSample->uvs();
                }
                if (wiresDirty || trianglesDirty ||
                    positionsDirty || normalsDirty || uvsDirty) {
                        if (wiresDirty) {
                            wireVertIndices = boost::shared_array<index_t>(
                                new index_t[2 * totalWires]);
                        }
                        if (trianglesDirty) {
                            triangleVertIndices.resize(totalTriangles.size());
                            for (size_t i = 0; i < totalTriangles.size(); i++) {
                                triangleVertIndices[i] = boost::shared_array<index_t>(
                                    new index_t[3 * totalTriangles[i]]);
                            }
                        }
                        if (positionsDirty) {
                            positions = boost::shared_array<float>(
                                new float[3 * totalVerts]);
                        }
                        if (normalsDirty) {
                            normals = boost::shared_array<float>(
                                new float[3 * totalVerts]);
                        }
                        if (uvsDirty) {
                            uvs.reset();
                            if (uvExists) {
                                uvs = boost::shared_array<float>(
                                    new float[2 * totalVerts]);
                            }
                        }
                        {
                            size_t wireIdx = 0;
                            size_t vertIdx = 0;
                            std::vector<size_t> triangleIdx(numIndexGroups, 0);
                            for (size_t i = 0; i < nbShapes; i++) {
                                const boost::shared_ptr<const ShapeSample>& sample =
                                    consolidatedShapes[i]->getSample(*timeIt);
                                const size_t numWires = sample->numWires();
                                const size_t numVerts = sample->numVerts();
                                
                                if (wiresDirty && sample->wireVertIndices()) {
                                    IndexBuffer::ReadInterfacePtr readable = sample->wireVertIndices()->readableInterface();
                                    const index_t* srcWireVertIndices = readable->get();
                                    for (size_t j = 0; j < numWires; j++) {
                                        wireVertIndices[2*(j + wireIdx) + 0] = index_t(srcWireVertIndices[2*j + 0] + vertIdx);
                                        wireVertIndices[2*(j + wireIdx) + 1] = index_t(srcWireVertIndices[2*j + 1] + vertIdx);
                                    }
                                }
                                
                                if (trianglesDirty) {
                                    for (size_t group = 0; group < numIndexGroups; group++) {
                                        const size_t numTriangles = sample->numTriangles(group);
                                        if (sample->triangleVertIndices(group)) {
                                            IndexBuffer::ReadInterfacePtr readable = sample->triangleVertIndices(group)->readableInterface();
                                            const index_t* srcTriangleVertIndices = readable->get();
                                            for (size_t j = 0; j < numTriangles; j++) {
                                                triangleVertIndices[group][3*(j + triangleIdx[group]) + 0] = index_t(srcTriangleVertIndices[3*j + 0] + vertIdx);
                                                triangleVertIndices[group][3*(j + triangleIdx[group]) + 1] = index_t(srcTriangleVertIndices[3*j + 1] + vertIdx);
                                                triangleVertIndices[group][3*(j + triangleIdx[group]) + 2] = index_t(srcTriangleVertIndices[3*j + 2] + vertIdx);
                                            }
                                        }
                                    }
                                }
                                
                                if (positionsDirty && sample->positions()) {
                                    VertexBuffer::ReadInterfacePtr readable = sample->positions()->readableInterface();
                                    memcpy(&positions[3*vertIdx], readable->get(),
                                        3*numVerts*sizeof(float));
                                }
                                
                                if (normalsDirty && sample->normals()) {
                                    VertexBuffer::ReadInterfacePtr readable = sample->normals()->readableInterface();
                                    memcpy(&normals[3*vertIdx], readable->get(),
                                        3*numVerts*sizeof(float));
                                }
                                
                                if (uvsDirty) {
                                    if (sample->uvs()) {
                                        VertexBuffer::ReadInterfacePtr readable = sample->uvs()->readableInterface();
                                        memcpy(&uvs[2*vertIdx], readable->get(),
                                            2*numVerts*sizeof(float));
                                    } else if (uvExists) {
                                        memset(&uvs[2*vertIdx], 0, 2*numVerts*sizeof(float));
                                    }
                                }
                                wireIdx += numWires;
                                vertIdx += numVerts;
                                for (size_t i = 0; i < numIndexGroups; i++) {
                                    triangleIdx[i] += sample->numTriangles(i);
                                }
                                boundingBox.
expand(sample->boundingBox());
                            }    
                        }
                }    
                std::vector<boost::shared_ptr<IndexBuffer> > newTriangleVertIndices(numIndexGroups);
                for (size_t i = 0; i < numIndexGroups; i++) {
                    newTriangleVertIndices[i] = IndexBuffer::create(
                        SharedArray<index_t>::create(
                            triangleVertIndices[i], 3 * totalTriangles[i]));
                }
                boost::shared_ptr<ShapeSample> newSample = ShapeSample::create(
                    *timeIt,
                    totalWires,
                    totalVerts,
                    IndexBuffer::create(
                        SharedArray<index_t>::create(
                            wireVertIndices, 2 * totalWires)),
                    newTriangleVertIndices,
                    VertexBuffer::createPositions(
                        SharedArray<float>::create(
                            positions, 3 * totalVerts)),
                    boundingBox,
                    diffuseColor,
                    visibility);
                if (normals) {
                    newSample->setNormals(
                        VertexBuffer::createNormals(
                            SharedArray<float>::create(
                                normals, 3 * totalVerts)));
                }
                if (uvs) {
                    newSample->setUVs(
                        VertexBuffer::createUVs(
                            SharedArray<float>::create(
                                uvs, 2 * totalVerts)));
                }
                newShape->addSample(newSample);
                timePrev = timeIt;
            }
            
            newShape->setMaterials(consolidatedShapes[0]->getMaterials());
            
            newShapes.push_back(newShape);
            consolidatedShapes.clear();
        }
        SubNode::MPtr fRootNode;
        const int     fThreshold;
        const bool    fMotionBlur;
        SubNode::MPtr fConsolidatedRootNode;
    };
    
    
    
    class SelectionChecker
    {
    public:
        {
            
            
            for (
unsigned int i = 0; i < selection.
length(); i++) {
 
                    fSelectionPaths.insert(fullDagPath);
                }
            }
            
            for (
unsigned int i = 0; i < selection.
length(); i++) {
 
                    fSelection.add(dagPath);
                }
            }
        }
        { return fSelection; }
    private:
        
        SelectionChecker(const SelectionChecker&);
        const SelectionChecker& operator= (const SelectionChecker&);
        {
            
                if (fSelectionPaths.find(fullDagPath) != fSelectionPaths.end()) {
                    return false;
                }
            }
            return checkGeometry(dagPath);
        }
        bool checkGeometry(
const MDagPath& dagPath)
 
        {
            
            if ((Baker::isBakeable(object) ||
                dagNode.
typeId() == ShapeNode::id) &&
                    return true;
            }
            
            bool hasGeometry = false;
            for (
unsigned int i = 0; i < dagPath.
childCount(); i++) {
 
                    continue;
                }
                if (checkGeometry(child)) {
                    hasGeometry= true;
                    break;
                }
            }
            return hasGeometry;
        }
        std::set<std::string> fSelectionPaths;
    };
    
    
    
    class ScopedPauseWorkerThread : boost::noncopyable
    {
    public:
        ScopedPauseWorkerThread()
        {
            GlobalReaderCache::theCache().pauseRead();
        }
        ~ScopedPauseWorkerThread()
        {
            GlobalReaderCache::theCache().resumeRead();
        }
    };
    
    
    
    
    
    
    class FileAndSubNode
    {
    public:
        SubNode::MPtr subNode;
        bool          isDummy;
        FileAndSubNode()
            : isDummy(false)
        {}
        FileAndSubNode(
const MFileObject& f, 
const SubNode::MPtr& n, 
bool d)
            : targetFile(f), subNode(n), isDummy(d)
        {}
    };
    typedef std::vector<FileAndSubNode> FileAndSubNodeList;
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    class NodePathRegistry : boost::noncopyable
    {
    public:
        enum Stages {
            kResolveStage,      
            kConstructStage,    
            kComplete           
        };
        NodePathRegistry(const bool     allDagObjects,
                         const bool     saveMultipleFiles,
            : fStage(kResolveStage),
              fAllDagObjects(allDagObjects),
              fSaveMultipleFiles(saveMultipleFiles),
              fDirectory(directory),
              fFilePrefix(filePrefix),
              fFileName(fileName),
              fClashOption(clashOption),
              fExtension(".abc")
        {}
        ~NodePathRegistry()
        {}
        
        
        {
            
            assert(fStage == kResolveStage);
            
            std::string fullPath = fullPathName(dagPath);
            assert(fPathMap.find(fullPath) == fPathMap.end());
            
            std::string parentFullPath = fullPathName(parentPath);
            
            PathEntry pathEntry;
            pathEntry.dagPath    = dagPath;
            pathEntry.parentPath = parentFullPath;
            fPathMap[fullPath] = pathEntry;
        }
        
        
        void resolve()
        {
            
            assert(fStage == kResolveStage);
            fStage = kConstructStage;
            
            BOOST_FOREACH (const PathMapType::value_type& v, fPathMap) {
                
                PathMapType::const_iterator it = fPathMap.find(v.second.parentPath);
                if (it == fPathMap.end()) {
                    assert(fRootMap.find(v.first) == fRootMap.end());
                    
                    std::string nodeName = dagNode.name().asChar();
                    
                    std::replace(nodeName.begin(), nodeName.end(), ':', '_');
                    
                    RootEntry rootEntry;
                    rootEntry.nodeName   = nodeName;
                    rootEntry.uniqueName = "Not_Specified";
                    rootEntry.sequence   = std::numeric_limits<size_t>::max();
                    rootEntry.overwrite  = true;
                    fRootMap[v.first] = rootEntry;
                }
            }
            
            std::map<std::string,size_t> nameTable;     
            BOOST_FOREACH (const RootMapType::value_type& v, fRootMap) {
                nameTable.insert(std::make_pair(v.second.nodeName, 0)).first->second++;
            }
            
            size_t counter = 0;
            BOOST_FOREACH (RootMapType::value_type& v, fRootMap) {
                std::string uniqueName = v.second.nodeName;
                
                
                if (nameTable.find(uniqueName)->second > 1) {
                    uniqueName = v.first.substr(1);  
                    std::replace(uniqueName.begin(), uniqueName.end(), '|', '_');
                    std::replace(uniqueName.begin(), uniqueName.end(), ':', '_');
                }
                v.second.uniqueName = uniqueName;
                v.second.sequence   = counter++;
            }
            
            const bool singleFile = fAllDagObjects || !fSaveMultipleFiles || fRootMap.size() == 1;
            const MString directory = validatedDirectory();
 
            const MString fileName  = validatedFileName();
 
            
            BOOST_FOREACH (RootMapType::value_type& v, fRootMap) {
                MString targetFileName = fFilePrefix;
 
                if (singleFile) {
                    
                    targetFileName += fileName;
                }
                else {
                    
                    if (fClashOption == "numericalIncrement") {
                        
                        targetFileName += (unsigned int)v.second.sequence;
                    }
                    else {
                        
                        targetFileName += v.second.uniqueName.c_str();
                    }
                }
                MString targetFullName = directory + 
"/" + targetFileName + fExtension;
 
                v.second.targetFile.setRawFullName(targetFullName);
            }
        }
        
        void promptOverwrite()
        {
            
            assert(fStage == kConstructStage);
            
                "if (!(`exists showGpuCacheExportConfirmDialog`))\n"
                "{\n"
                "    eval(\"source \\\"doGpuCacheExportArgList.mel\\\"\");\n"
                "}\n"
            );
            
            const bool singleFile = fAllDagObjects || !fSaveMultipleFiles || fRootMap.size() == 1;
            enum OverwriteChoice { kUnknownOverwrite, kAlwaysOverwrite, kNeverOverwrite };
            OverwriteChoice choice = kUnknownOverwrite;
            BOOST_FOREACH (RootMapType::value_type& v, fRootMap) {
                
                if (!v.second.targetFile.exists()) continue;
                if (choice == kUnknownOverwrite) {
                    
                        "showGpuCacheExportConfirmDialog \"" + 
                        EncodeString(v.second.targetFile.resolvedFullName()) + 
                        "\""
                    );
                    if (result == "yes") {
                        
                        v.second.overwrite = true;
                        
                        
                        if (singleFile) choice = kAlwaysOverwrite;
                    }
                    else if (result == "no") {
                        
                        v.second.overwrite = false;
                        
                        
                        if (singleFile) choice = kNeverOverwrite;
                    }
                    else if (result == "yesAll") {
                        
                        v.second.overwrite = true;
                        choice = kAlwaysOverwrite;
                    }
                    else if (result == "noAll" || result == "dismiss") {
                        
                        v.second.overwrite = false;
                        choice = kNeverOverwrite;
                    }
                    else {
                        
                        
                        assert(0);
                    }
                }
                else if (choice == kAlwaysOverwrite) {
                    v.second.overwrite = true;
                }
                else if (choice == kNeverOverwrite) {
                    v.second.overwrite = false;
                }
                else {
                    assert(0);
                }
            }
        }
        
        void associateSubNode(
const MDagPath& dagPath, 
const SubNode::MPtr& subNode)
 
        {
            assert(fStage == kConstructStage);
            assert(subNode);
            
            std::string fullPath = fullPathName(dagPath);
            PathMapType::iterator it = fPathMap.find(fullPath);
            assert(it != fPathMap.end());
            if (it != fPathMap.end()) {
                it->second.subNode = subNode;
            }
        }
        
        void constructHierarchy()
        {
            assert(fStage == kConstructStage);
            fStage = kComplete;
            
            
            BOOST_FOREACH (const PathMapType::value_type& v, fPathMap) {
                
                const SubNode::MPtr& thisSubNode = v.second.subNode;
                
                PathMapType::iterator parentIt = fPathMap.find(v.second.parentPath);
                if (parentIt != fPathMap.end()) {
                    
                    const SubNode::MPtr& parentSubNode = parentIt->second.subNode;
                    SubNode::connect(parentSubNode, thisSubNode);
                }
                else {
                    
                    RootMapType::iterator rootIt = fRootMap.find(v.first);
                    assert(rootIt != fRootMap.end());
                    if (rootIt != fRootMap.end()) {
                        rootIt->second.subNode = thisSubNode;
                    }
                }
            }
        }
        
        void generateFileAndSubNodes(FileAndSubNodeList& list)
        {
            
            assert(fStage == kComplete);
            
            
            
            
            
            if (fAllDagObjects || !fSaveMultipleFiles) {
                if (!fRootMap.empty() && fRootMap.begin()->second.overwrite) {
                    
                    GroupCreator groupCreator;
                    BOOST_FOREACH (const RootMapType::value_type& v, fRootMap) {
                        
                        
                        v.second.subNode->setName(v.second.uniqueName.c_str());
                        
                        groupCreator.addChild(v.second.subNode);
                    }
                    groupCreator.group();
                    
                    
                    
                    const RootEntry& rootEntry = fRootMap.begin()->second;
                    const MString rootNodeName = (fRootMap.size() == 1) ?
 
                        rootEntry.subNode->getName() : getSceneNameAsValidObjectName();
                    
                    list.push_back(FileAndSubNode(
                        rootEntry.targetFile,
                        groupCreator.getSubNode(rootNodeName),
                        true 
                    ));
                }
            }
            else {
                
                BOOST_FOREACH (const RootMapType::value_type& v, fRootMap) {
                    
                    if (!v.second.overwrite) continue;
                    list.push_back(FileAndSubNode(
                        v.second.targetFile,
                        v.second.subNode,
                        false 
                    ));
                }
            }
        }
        void dump()
        {
            using namespace std;
            stringstream out;
            out << "Current Stage: ";
            switch (fStage) {
            case kResolveStage:     out << "ResolveStage";      break;
            case kConstructStage:   out << "ConstructStage";    break;
            case kComplete:         out << "Complete";          break;
            }
            out << endl;
            out << "Path Map: " << endl;
            BOOST_FOREACH (const PathMapType::value_type& v, fPathMap) {
                out << "Path: "
                    << v.first
                    << ", Parent: "
                    << v.second.parentPath
                    << ", SubNode: "
                    << (void*)v.second.subNode.get()
                    << endl;
            }
            out << "Root Map: " << endl;
            BOOST_FOREACH (const RootMapType::value_type& v, fRootMap) {
                out << "Path: "
                    << v.first
                    << ", Node: "
                    << v.second.nodeName
                    << ", Unique: "
                    << v.second.uniqueName
                    << ", Sequence: "
                    << v.second.sequence
                    << ", Overwrite: "
                    << v.second.overwrite
                    << ", Target: "
                    << v.second.targetFile.resolvedFullName().asChar()
                    << ", SubNode: "
                    << (void*)v.second.subNode.get()
                    << endl;
            }
            cout << out.str() << endl;
        }
    private:
        static std::string fullPathName(
const MDagPath& dagPath)
 
        {
            return std::string(buffer.
asChar());
 
        }
        {
            if (fDirectory.length() > 0) {
                
            }
            else {
                const MString alembicFileRule = 
"alembicCache";
 
                const MString alembicFilePath = 
"cache/alembic";
 
                queryFileRuleCmd.
format(
"workspace -q -fre \"^1s\"", alembicFileRule);
                queryFolderCmd.
format(
"workspace -en `workspace -q -fre \"^1s\"`", alembicFileRule);
                
                    
                }
                else {
                    
                    addFileRuleCmd.
format(
"workspace -fr \"^1s\" \"^2s\"", alembicFileRule, alembicFilePath);
                    
                    
                }
                
                if (expandName.
length() == 0) {
 
                    expandName = alembicFilePath;
                }
            }
        }
        {
            if (fFileName.length() > 0) {
                
                fileName = fFileName;
            }
            else {
                
                if (fRootMap.size() == 1) {
                    fileName = fRootMap.begin()->second.uniqueName.c_str();
                }
                else {
                    fileName = getSceneName();
                }
            }
            return fileName;
        }
        
        struct PathEntry {
            std::string   parentPath;
            SubNode::MPtr subNode;
        };
        typedef boost::unordered_map<std::string,PathEntry> PathMapType;
        
        struct RootEntry {
            std::string   nodeName;
            std::string   uniqueName;
            size_t        sequence;
            bool          overwrite;
            SubNode::MPtr subNode;
        };
        typedef boost::unordered_map<std::string,RootEntry> RootMapType;
        Stages      fStage;
        PathMapType fPathMap;
        RootMapType fRootMap;
        const bool    fAllDagObjects;
        const bool    fSaveMultipleFiles;
    };
}
namespace GPUCache {
void* Command::creator()
{
    return new Command();
}
{
    syntax.
addFlag(
"-o",   
"-optimize"                                   );
    syntax.
addFlag(
"-sf",  
"-showFailed"                                 );
    syntax.
addFlag(
"-ss",  
"-showStats"                                  );
    syntax.
addFlag(
"-sgs", 
"-showGlobalStats"                            );
    syntax.
addFlag(
"-atr", 
"-animTimeRange"                              );
    syntax.
addFlag(
"-gma", 
"-gpuManufacturer"                            );
    syntax.
addFlag(
"-gmo", 
"-gpuModel"                                   );
    syntax.
addFlag(
"-gdv", 
"-gpuDriverVersion"                           );
    syntax.
addFlag(
"-gms", 
"-gpuMemorySize"                              );
    syntax.
addFlag(
"-ado", 
"-allDagObjects"                              );
    syntax.
addFlag(
"-ra",  
"-refreshAll"                                 );
    syntax.
addFlag(
"-rs",  
"-refreshSettings"                            );
    syntax.
addFlag(
"-wbr", 
"-waitForBackgroundReading"                   );
    syntax.
addFlag(
"-wm",  
"-writeMaterials"                             );
    syntax.
addFlag(
"-wuv", 
"-writeUVs"                                   );
    syntax.
addFlag(
"-omb", 
"-optimizeAnimationsForMotionBlur"            );
    syntax.
addFlag(
"-ubt", 
"-useBaseTessellation"                        );
    syntax.
addFlag(
"-lfe", 
"-listFileEntries"                            );
    syntax.
addFlag(
"-lse", 
"-listShapeEntries"                           );
    return syntax;
}
Command::Command()
    : fMode(kCreate)
{}
Command::~Command()
{}
bool Command::isUndoable() const
{
    return false;
}
bool Command::hasSyntax() const
{
    return true;
}
void Command::AddHierarchy(
    std::map<std::string, int>*          idMap,
    std::vector<MObject>*                sourceNodes,
    std::vector<std::vector<MDagPath> >* sourcePaths,
    std::vector<MObject>*                gpuCacheNodes)
{
    MStatus status = dagNode.getPath(firstDagPath);
 
    std::string firstPath(firstDagPath.partialPathName().asChar());
    std::map<std::string, int>::iterator pos = idMap->find(firstPath);
    if (pos != idMap->end()){
        
        (*sourcePaths)[pos->second].push_back(dagPath);
    }
    else {
        bool isWarning = true;
        if (dagNode.typeId() == ShapeNode::id) {
            if (fMode == kCreate) {
                
                (*idMap)[firstPath] = (int)sourceNodes->size();
                sourceNodes->push_back(object);
                sourcePaths->push_back(std::vector<MDagPath>(1, dagPath));
            }
            else {
                
                gpuCacheNodes->push_back(object);
            }
        }
        else if (Baker::isBakeable(object)) {
            (*idMap)[firstPath] = (int)sourceNodes->size();
            sourceNodes->push_back(object);
            sourcePaths->push_back(std::vector<MDagPath>(1, dagPath));
            if (fMode != kCreate && fShowFailedFlag.isSet()) {
            }
        }
        else if (fShowFailedFlag.isSet()) {
        }
        if (msgFmt.length() > 0) {
            MString nodeName = firstDagPath.fullPathName();
 
            if (isWarning) {
            }
            else {
            }
        }
    }
    for(unsigned int i = 0; i < numChild; ++i) {
        if (!childNode.isIntermediateObject())
            AddHierarchy(childPath, idMap, sourceNodes, sourcePaths, gpuCacheNodes);
    }
}
bool Command::AddSelected(
    std::vector<MObject>*                sourceNodes,
    std::vector<std::vector<MDagPath> >* sourcePaths,
    std::vector<MObject>*                gpuCacheNodes)
{
    
    std::map<std::string, int> idMap;
    for (
unsigned int i = 0; i<objects.
length(); ++i) {
 
            AddHierarchy(sourceDagPath, &idMap, sourceNodes, sourcePaths, gpuCacheNodes);
        }
    }
    if (fMode == kCreate) {
        if (sourceNodes->empty()) {
            if (gpuCacheNodes->empty()) {
            }
            else {
            }
            displayWarning(msg);
            return false;
        }
        return true;
    }
    else {
        if (!fRefreshSettingsFlag.isSet() && gpuCacheNodes->empty()) {
            if (sourceNodes->empty()) {
            }
            else {
            }
            displayWarning(msg);
            return false;
        }
        return true;
    }
}
{
    if (!status) return status;
    unsigned int numFlags = 0;
    
    if (argsDb.isEdit()) {
        if (argsDb.isQuery()) {
            displayError(msg);
        }
        fMode = kEdit;
        numFlags++;
    }
    else if (argsDb.isQuery()) {
        fMode = kQuery;
        numFlags++;
    }
    numFlags += fDirectoryFlag.parse(argsDb, "-directory");
    if (!fDirectoryFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fFileNameFlag.parse(argsDb, "-fileName");
    if (!fFileNameFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fSaveMultipleFilesFlag.parse(argsDb, "-saveMultipleFiles");
    if (!fSaveMultipleFilesFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fFilePrefixFlag.parse(argsDb, "-filePrefix");
    if (!fFilePrefixFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fClashOptionFlag.parse(argsDb, "-clashOption");
    if (!fClashOptionFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fOptimizeFlag.parse(argsDb, "-optimize");
    if (!fOptimizeFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fOptimizationThresholdFlag.parse(argsDb, "-optimizationThreshold");
    if (!fOptimizationThresholdFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fStartTimeFlag.parse(argsDb, "-startTime");
    if (!fStartTimeFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fEndTimeFlag.parse(argsDb, "-endTime");
    if (!fEndTimeFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fSimulationRateFlag.parse(argsDb, "-simulationRate");
    if (!fSimulationRateFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    if (fSimulationRateFlag.isSet()) {
        if (fSimulationRateFlag.arg() < minRate) {
            
            displayError(msg);
        }
    }
    numFlags += fSampleMultiplierFlag.parse(argsDb, "-sampleMultiplier");
    if (!fSampleMultiplierFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    if (fSampleMultiplierFlag.isSet() && fSampleMultiplierFlag.arg() <= 0) {
        displayError(msg);
    }
    numFlags += fCompressLevelFlag.parse(argsDb, "-compressLevel");
    if (!fCompressLevelFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fDataFormatFlag.parse(argsDb, "-dataFormat");
    if (!fDataFormatFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fShowFailedFlag.parse(argsDb, "-showFailed");
    assert(fShowFailedFlag.isModeValid(fMode));
    numFlags += fShowStats.parse(argsDb, "-showStats");
    assert(fShowStats.isModeValid(fMode));
    numFlags += fShowGlobalStats.parse(argsDb, "-showGlobalStats");
    assert(fShowGlobalStats.isModeValid(fMode));
    numFlags += fDumpHierarchy.parse(argsDb, "-dumpHierarchy");
    assert(fDumpHierarchy.isModeValid(fMode));
    numFlags += fAnimTimeRangeFlag.parse(argsDb, "-animTimeRange");
    if (!fAnimTimeRangeFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fAllDagObjectsFlag.parse(argsDb, "-allDagObjects");
    if (!fAllDagObjectsFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fRefreshFlag.parse(argsDb, "-refresh");
    if (!fRefreshFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fRefreshAllFlag.parse(argsDb, "-refreshAll");
    if (!fRefreshAllFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fListFileEntriesFlag.parse(argsDb, "-listFileEntries");
    if (!fListFileEntriesFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fListShapeEntriesFlag.parse(argsDb, "-listShapeEntries");
    if (!fListShapeEntriesFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fRefreshSettingsFlag.parse(argsDb, "-refreshSettings");
    if (!fRefreshSettingsFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fGpuManufacturerFlag.parse(argsDb, "-gpuManufacturer");
    if (!fGpuManufacturerFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fGpuModelFlag.parse(argsDb, "-gpuModel");
    if (!fGpuModelFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fGpuDriverVersion.parse(argsDb, "-gpuDriverVersion");
    if (!fGpuDriverVersion.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fGpuMemorySize.parse(argsDb, "-gpuMemorySize");
    if (!fGpuMemorySize.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fWaitForBackgroundReadingFlag.parse(argsDb, "-waitForBackgroundReading");
    if (!fWaitForBackgroundReadingFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fWriteMaterials.parse(argsDb, "-writeMaterials");
    if (!fWriteMaterials.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fUVsFlag.parse(argsDb, "-writeUVs");
    if( !fUVsFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fOptimizeAnimationsForMotionBlurFlag.parse(argsDb, "-optimizeAnimationsForMotionBlur");
    if (!fOptimizeAnimationsForMotionBlurFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fUseBaseTessellationFlag.parse(argsDb, "-useBaseTessellation");
    if (!fUseBaseTessellationFlag.isModeValid(fMode)) {
        displayError(msg);
    }
    numFlags += fPromptFlag.parse(argsDb, "-prompt");
    if (fRefreshAllFlag.isSet())
    {
        
        
        
        
        
        if( numFlags > 1 )
        {
            displayError(msg);
        }
        refreshAll();
    }
    if (fListFileEntriesFlag.isSet())
    {
        if( numFlags > 1 )
        {
            displayError(msg);
        }
        listFileEntries();
    }
    if (fListShapeEntriesFlag.isSet())
    {
        if( numFlags > 1 )
        {
            displayError(msg);
        }
        listShapeEntries();
    }
    
    if (fAllDagObjectsFlag.isSet()) {
        
        for (
unsigned int i = 0; i < result.
length(); i++) {
 
        }
    }
    else {
        
        
        status = argsDb.getObjects(selectedObjectArgs);
        MStatError(status, "argsDb.getObjects()");
        if (!selectedObjectArgs.
isEmpty()) {
 
            MStatError(status, "objects.merge()");
        }
    }
            !(fMode == kQuery && fShowGlobalStats.isSet()) &&
            !(fMode == kEdit && fRefreshSettingsFlag.isSet()) &&
            !(fMode == kQuery && fGpuManufacturerFlag.isSet()) &&
            !(fMode == kQuery && fGpuModelFlag.isSet()) &&
            !(fMode == kQuery && fGpuDriverVersion.isSet()) &&
            !(fMode == kQuery && fGpuMemorySize.isSet())
            ) {
    }
    {
        SelectionChecker selectionChecker(objects);
        objects = selectionChecker.selection();
    }
    std::vector<MObject>                sourceNodes;
    std::vector<std::vector<MDagPath> > sourcePaths;
    std::vector<MObject>                gpuCacheNodes;
    if (fMode == kCreate || fMode == kEdit || fShowStats.isSet() ||
            fDumpHierarchy.isSet() || fAnimTimeRangeFlag.isSet() ||
            fWaitForBackgroundReadingFlag.isSet()) {
        if (!AddSelected(objects, &sourceNodes, &sourcePaths, &gpuCacheNodes))
    }
    
    
    
    
    
    
    
    switch (fMode) {
        case kCreate:   status = doCreate(sourceNodes, sourcePaths, objects); break;
        case kEdit:     status = doEdit(gpuCacheNodes);     break;
        case kQuery:    status = doQuery(gpuCacheNodes);    break;
    }
    
    return status;
}
MStatus Command::doCreate(
const std::vector<MObject>&                sourceNodes,
 
                               const std::vector<std::vector<MDagPath> >& sourcePaths,
{
    
    
    MCheckReturn(
        Command::doBaking(
            sourceNodes,
            sourcePaths,
            fSampleMultiplierFlag.arg(1)));
}
MStatus Command::doQuery(
const std::vector<MObject>& gpuCacheNodes)
 const 
{
    
    if (fShowStats.isSet() ||
        fShowGlobalStats.isSet() ||
        fDumpHierarchy.isSet()
    ) {
        
        if (fAnimTimeRangeFlag.isSet()) {
        }
        if (fShowStats.isSet()) {
            showStats(gpuCacheNodes, result);
        }
        if (fShowGlobalStats.isSet()) {
            showGlobalStats(result);
        }
        if (fDumpHierarchy.isSet()) {
            if (fDumpHierarchy.isArgValid()) {
                
                MCheckReturn( dumpHierarchyToFile(gpuCacheNodes, file) );
            }
            else {
                
                dumpHierarchy(gpuCacheNodes, result);
            }
        }
        {
            for (
unsigned int i = 0; i < result.
length(); i++) {
 
                if (i > 0) output += "\n";
                output += result[i];
            }
        }
    }
    else if (fAnimTimeRangeFlag.isSet()) {
        
        showAnimTimeRange(gpuCacheNodes, animTimeRange);
    }
    else if (fGpuManufacturerFlag.isSet()) {
    }
    else if (fGpuModelFlag.isSet()) {
    }
    else if (fGpuDriverVersion.isSet()) {
        int driverVersion[3];
        VramQuery::driverVersion(driverVersion);
        verionStr += driverVersion[0];
        verionStr += ".";
        verionStr += driverVersion[1];
        verionStr += ".";
        verionStr += driverVersion[2];
    }
    else if (fGpuMemorySize.isSet()) {
    }
    else if (fWaitForBackgroundReadingFlag.isSet()) {
        
        BOOST_FOREACH (
const MObject& node, gpuCacheNodes) {
            
            ShapeNode* shapeNode = (ShapeNode*)dagNode.userNode();
            if (shapeNode) {
                shapeNode->getCachedGeometry();
            }
            
            GlobalReaderCache::theCache().waitForRead(shapeNode->getCacheFileEntry().get());
            
            if (shapeNode) {
                shapeNode->getCachedGeometry();
            }
        }
    }
}
MStatus Command::doEdit(
const std::vector<MObject>& gpuCacheNodes)
 
{
    if (fRefreshSettingsFlag.isSet()) {
        Config::refresh();
    }
    if (fRefreshFlag.isSet()) {
        refresh(gpuCacheNodes);
    }
}
    const std::vector<MObject>&                sourceNodes,
    const std::vector<std::vector<MDagPath> >& sourcePaths,
    int                         samplingRate   
)
{
    
    if (startTime > endTime) {
    }
    
    NodePathRegistry pathRegistry(
        fAllDagObjectsFlag.isSet(),
        fSaveMultipleFilesFlag.arg(true),
        fDirectoryFlag.arg(),
        fFilePrefixFlag.arg(),
        fFileNameFlag.arg(),
        fClashOptionFlag.arg()
    );
    BOOST_FOREACH (const std::vector<MDagPath>& dagPaths, sourcePaths) {
        BOOST_FOREACH (
const MDagPath& path, dagPaths) {
            pathRegistry.add(path);
        }
    }
    pathRegistry.resolve();
    
        pathRegistry.promptOverwrite();
    }
    
    ProgressBar progressBar(kExportingMsg,
        (unsigned int)(sourceNodes.size() * (int)(
    
    
    MTime currentTime = startTime;
 
    
    typedef std::vector< boost::shared_ptr<Baker> > Bakers;
    Bakers bakers;
    
    boost::shared_ptr<MaterialBaker> materialBaker;
    if (fWriteMaterials.isSet()) {
        materialBaker = boost::make_shared<MaterialBaker>();
    }
    for (size_t i = 0; i < sourceNodes.size(); i++) {
        
        const boost::shared_ptr<Baker> baker(
            Baker::create(sourceNodes[i], sourcePaths[i]));
        if (!baker) {
        }
        const boost::shared_ptr<ShapeBaker> shapeBaker = boost::dynamic_pointer_cast<ShapeBaker,Baker>( baker );
        if( shapeBaker && fUVsFlag.isSet() ) 
        {
            shapeBaker->enableUVs();
        }
        if (materialBaker) {
            baker->setWriteMaterials();
        }
        if (fUseBaseTessellationFlag.isSet()) {
            baker->setUseBaseTessellation();
        }
        bakers.push_back(baker);
        
        MCheckReturn(baker->sample(currentTime));
        
        if (materialBaker) {
            BOOST_FOREACH (
const MDagPath& path, sourcePaths[i]) {
                    MCheckReturn( materialBaker->addShapePath(path) );
                }
            }
        }
        MUpdateProgressAndCheckInterruption(progressBar);
    }
    
    if (materialBaker) {
        MCheckReturn( materialBaker->sample(currentTime) );
    }
    
    currentTime += simulationRate;
    for (int sampleIdx = 1; currentTime<=endTime;
         currentTime += simulationRate, ++sampleIdx) {
        
        if (sampleIdx % samplingRate == 0) {
            BOOST_FOREACH(const boost::shared_ptr<Baker>& baker, bakers) {
                MCheckReturn(baker->sample(currentTime));
                MUpdateProgressAndCheckInterruption(progressBar);
            }    
            if (materialBaker) {
                MCheckReturn( materialBaker->sample(currentTime) );
            }
        }
    }    
    
    MaterialGraphMap::Ptr materials;
    if (materialBaker) {
        materialBaker->buildGraph();
        materials = materialBaker->get();
    }
    
    {
        assert(bakers.size() == sourceNodes.size());
        assert(bakers.size() == sourcePaths.size());
        
        for (size_t i = 0; i < sourcePaths.size(); i++) {
            for (size_t j = 0; j < sourcePaths[i].size(); j++) {
                const MDagPath& path  = sourcePaths[i][j];
 
                SubNode::MPtr subNode = bakers[i]->getNode(j);
                pathRegistry.associateSubNode(path, subNode);
            }
        }
        pathRegistry.constructHierarchy();
    }
    
    bakers.clear();
    materialBaker.reset();
    
    
    FileAndSubNodeList fileList;
    pathRegistry.generateFileAndSubNodes(fileList);
    
    if (fOptimizeFlag.isSet()) {
        const int  threshold  = fOptimizationThresholdFlag.arg(40000);
        const bool motionBlur = fOptimizeAnimationsForMotionBlurFlag.isSet();
        BOOST_FOREACH (FileAndSubNode& v, fileList) {
            Consolidator consolidator(v.subNode, threshold, motionBlur);
            MCheckReturn( consolidator.consolidate() );
            SubNode::MPtr consolidatedRootNode = consolidator.consolidatedRootNode();
            if (consolidatedRootNode) {
                v.subNode = consolidatedRootNode;
                v.isDummy = false;
            }
        }
    }
    
    
    
    
    progressBar.reset(kWritingMsg, (unsigned int)fileList.size());
    
    const MTime timePerCycle = simulationRate * samplingRate;
 
    Writer gpuCacheWriter(
        (char)fCompressLevelFlag.arg(-1),
        fDataFormatFlag.arg("hdf"),
        timePerCycle,
        startTime
    );
    BOOST_FOREACH (const FileAndSubNode& v, fileList) {
        if (v.isDummy) {
            
            MCheckReturn(
                gpuCacheWriter.writeNodes(
                    v.subNode->getChildren(),
                    materials,
                    v.targetFile
                )
            );
        }
        else {
            
            MCheckReturn(
                gpuCacheWriter.writeNode(
                    v.subNode,
                    materials,
                    v.targetFile
                )
            );
        }
        appendToResult(v.targetFile.resolvedFullName());
        MUpdateProgressAndCheckInterruption(progressBar);
    }
}
void Command::showStats(
    const std::vector<MObject>& gpuCacheNodes,
) const
{
    {
        StatsVisitor stats;
        BOOST_FOREACH(
const MObject& gpuCacheObject, gpuCacheNodes) {
            MPxNode* node = gpuCacheFn.userNode();
 
            assert(node);
            assert(dynamic_cast<ShapeNode*>(node));
            ShapeNode* gpuCacheNode =
                static_cast<ShapeNode*>(node);
            stats.accumulateNode(gpuCacheNode->getCachedGeometry());
            stats.accumulateMaterialGraph(gpuCacheNode->getCachedMaterial());
        }
        stats.print(result, false);
    }
    {
        BOOST_FOREACH(
const MObject& gpuCacheObject, gpuCacheNodes) {
            MPxNode* node = gpuCacheFn.userNode();
 
            assert(node);
            assert(dynamic_cast<ShapeNode*>(node));
            ShapeNode* gpuCacheNode =
                static_cast<ShapeNode*>(node);
            stats.accumulateNode(gpuCacheNode->getCachedGeometry());
            stats.accumulateMaterialGraph(gpuCacheNode->getCachedMaterial());
        }
        stats.print(result, true);
    }
}
void Command::showGlobalStats(
) const
{
    
    const size_t unitBoundingBoxIndicesBytes = UnitBoundingBox::indices()->bytes();
    const size_t unitBoundingBoxPositionsBytes = UnitBoundingBox::positions()->bytes();
    const size_t unitBoundingBoxBytes = unitBoundingBoxIndicesBytes + unitBoundingBoxPositionsBytes;
    const size_t unitBoundingBoxNbIndices = 1;
    const size_t unitBoundingBoxNbVertices = 1;
    const size_t unitBoundingBoxNbBuffers = unitBoundingBoxNbIndices + unitBoundingBoxNbVertices;
    
    {
        double  memSize =
            toHumanUnits(IndexBuffer::nbAllocatedBytes() +
                         VertexBuffer::nbAllocatedBytes() -
                         unitBoundingBoxBytes,
                         memUnit);
        MString msg_buffers; msg_buffers += (double)(IndexBuffer::nbAllocated() +
 
                                                     VertexBuffer::nbAllocated() -
                                                     unitBoundingBoxNbBuffers);
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    {
        double  memSize =
            toHumanUnits(IndexBuffer::nbAllocatedBytes() - unitBoundingBoxIndicesBytes, memUnit);
        MString msg_buffers; msg_buffers += (double)(IndexBuffer::nbAllocated() - unitBoundingBoxNbIndices);
 
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    {
        double  memSize =
            toHumanUnits(VertexBuffer::nbAllocatedBytes() - unitBoundingBoxPositionsBytes, memUnit);
        MString msg_buffers; msg_buffers +=
 
                                 (double)(VertexBuffer::nbAllocated() - unitBoundingBoxNbVertices);
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    
    {
        double  memSize = toHumanUnits(VBOBuffer::nbAllocatedBytes(),
                                       memUnit);
        MString msg_buffers; msg_buffers += (double)(VBOBuffer::nbAllocated());
 
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    {
        double  memSize = toHumanUnits(VBOBuffer::nbIndexAllocatedBytes(),
                                       memUnit);
        MString msg_buffers; msg_buffers +=
 
                                 (double)VBOBuffer::nbIndexAllocated();
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    {
        double  memSize = toHumanUnits(VBOBuffer::nbVertexAllocatedBytes(),
                                       memUnit);
        MString msg_buffers; msg_buffers +=
 
                                 (double)VBOBuffer::nbVertexAllocated();
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    
    {
    }
    {
        double  memSize = toHumanUnits(VBOBuffer::nbUploadedBytes(),
                                       memUnit);
        MString msg_buffers; msg_buffers +=
 
                                 (double)VBOBuffer::nbUploaded();
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
    {
        double  memSize = toHumanUnits(VBOBuffer::nbEvictedBytes(),
                                       memUnit);
        MString msg_buffers; msg_buffers +=
 
                                 (double)VBOBuffer::nbEvicted();
        MString msg_memSize; msg_memSize += memSize;
 
            msg_buffers, msg_memSize, memUnit);
    }
}
void Command::dumpHierarchy(
    const std::vector<MObject>& gpuCacheNodes,
) const
{
    BOOST_FOREACH(
const MObject& gpuCacheObject, gpuCacheNodes) {
        MPxNode* node = gpuCacheFn.userNode();
 
        assert(node);
        assert(dynamic_cast<ShapeNode*>(node));
        ShapeNode* gpuCacheNode =
            static_cast<ShapeNode*>(node);
        SubNode::Ptr rootNode = gpuCacheNode->getCachedGeometry();
        if (rootNode) {
            DumpHierarchyVisitor visitor(result);
            rootNode->accept(visitor);
        }
        MaterialGraphMap::Ptr materials = gpuCacheNode->getCachedMaterial();
        if (materials) {
            DumpMaterialVisitor visitor(result);
            visitor.dumpMaterials(materials);
        }
    }
}
MStatus Command::dumpHierarchyToFile(
 
    const std::vector<MObject>& gpuCacheNodes,
) const
{
    dumpHierarchy(gpuCacheNodes, result);
    if (!output.is_open()) {
    }
    for (
unsigned int i = 0; i < result.
length(); i++) {
 
        output << result[i].asChar() << std::endl;
    }
    output.close();
}
void Command::showAnimTimeRange(
    const std::vector<MObject>& gpuCacheNodes,
) const
{
    TimeInterval animTimeRange(TimeInterval::kInvalid);
    BOOST_FOREACH(
const MObject& node, gpuCacheNodes) {
        if (dagNode.typeId() != ShapeNode::id) {
            continue;
        }
        ShapeNode* userNode = dynamic_cast<ShapeNode*>(dagNode.userNode());
        if (userNode == NULL) {
            continue;
        }
        const SubNode::Ptr topNode = userNode->getCachedGeometry();
        if (userNode->backgroundReadingState() != CacheFileEntry::kReadingDone) {
            
            
                
                ScopedPauseWorkerThread pause;
                GlobalReaderCache::CacheReaderProxy::Ptr proxy =
                    GlobalReaderCache::theCache().getCacheReaderProxy(cacheFile);
                GlobalReaderCache::CacheReaderHolder holder(proxy);
                boost::shared_ptr<CacheReader> reader = holder.getCacheReader();
                if (reader && reader->valid()) {
                    TimeInterval interval(TimeInterval::kInvalid);
                    if (reader->readAnimTimeRange(interval)) {
                        animTimeRange |= interval;
                    }
                }
            }
        }
        else if (topNode) {
            const SubNodeData::Ptr data = topNode->getData();
            if (data) {
                animTimeRange |= data->animTimeRange();
            }
        }
    }
}
void Command::refresh(const std::vector<MObject>& gpuCacheNodes)
{
    BOOST_FOREACH(
const MObject& node, gpuCacheNodes) {
        if (dagNode.typeId() != ShapeNode::id) {
            continue;
        }
        ShapeNode* userNode = dynamic_cast<ShapeNode*>(dagNode.userNode());
        if (userNode == NULL) {
            continue;
        }
        userNode->refreshCachedGeometry( true );
    }
    
    
    }
}
void Command::refreshAll()
{
    
    CacheFileRegistry::theCache().clear();
    
    std::vector<MObjectHandle> shapes;
    CacheShapeRegistry::theCache().getAll( shapes );
    for( size_t i = 0; i < shapes.size(); i++ )
    {
        if( !shapes[i].isValid() )
        {
            continue;
        }
        assert( nodeFn.
typeId() == ShapeNode::id );
        ShapeNode* shape = (ShapeNode*) nodeFn.
userNode();
        assert( shape );
        
        shape->refreshCachedGeometry( false );
    }
    
    
    }
}
void Command::listFileEntries()
{
    std::vector<CacheFileEntry::MPtr> entries;
    CacheFileRegistry::theCache().getAll(entries);
    size_t nEntries = entries.size();
    for( size_t i = 0; i < nEntries; i++ )
    {
        output.
append( entries[i]->fResolvedCacheFileName );
    }
}
void Command::listShapeEntries()
{
    std::vector<MObjectHandle> shapes;
    CacheShapeRegistry::theCache().getAll(shapes);
    size_t nShapes = shapes.size();
    for( size_t i = 0; i < nShapes; i++ )
    {
        if( stat )
        {
            ShapeNode* shapeNode = (ShapeNode*) nodeFn.
userNode();
            CacheFileEntry::MPtr entry = shapeNode->getCacheFileEntry();
            str += ":";
            if( entry )
            {
                str += entry->fResolvedCacheFileName;
            }
        }
        else
        {
            str += "kNullObj:";
        }
    }
}
}