#include "gpuCacheSubSceneOverride.h"
#include "gpuCacheShapeNode.h"
#include "gpuCacheUnitBoundingBox.h"
#include "gpuCacheFrustum.h"
#include "gpuCacheUtil.h"
#include "CacheReader.h"
#include <boost/foreach.hpp>
#include <boost/unordered_set.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <tbb/tbb_thread.h>
#include <tbb/mutex.h>
#include <maya/MDagMessage.h>
#include <maya/MDGMessage.h>
#include <maya/MModelMessage.h>
#include <maya/MNodeMessage.h>
#include <maya/MSceneMessage.h>
#include <maya/MEventMessage.h>
#include <maya/MHWGeometryUtilities.h>
#include <maya/MAnimControl.h>
#include <maya/MDrawContext.h>
#include <maya/MFnAttribute.h>
#include <maya/MFnDagNode.h>
#include <maya/MGlobal.h>
#include <maya/MItDag.h>
#include <maya/MSelectionContext.h>
#include <maya/MSelectionList.h>
#include <maya/MShaderManager.h>
#include <maya/MUserData.h>
namespace {
using namespace GPUCache;
template<typename T>
class ScopedGuard : boost::noncopyable
{
public:
    ScopedGuard(T& value)
        : fValueRef(value), fValueBackup(value)
    {}
    ~ScopedGuard()
    {
        fValueRef = fValueBackup;
    }
private:
    T& fValueRef;
    T  fValueBackup;
};
tbb::tbb_thread::id gsMainThreadId = tbb::this_tbb_thread::get_id();
{
    return mayaBuffer->
size();
 
}
{
}
template < typename T, typename C >
class MayaBufferArray : public Array<T>
{
    
    
    
    
    
    
    class TempCopyReadableInterface : public ArrayReadInterface<T>
    {
         boost::shared_array<T> fLocalArray;
         GPUCACHE_DECLARE_MAKE_SHARED_FRIEND_1;
    public:
        TempCopyReadableInterface(boost::shared_array<T> localArray) : fLocalArray(localArray) {}
        virtual ~TempCopyReadableInterface() {}
        virtual const T* get() const { return fLocalArray.get(); }
    };
public:
    typedef typename Array<T>::Digest Digest;
    
    static boost::shared_ptr<Array<T> > create(const boost::shared_ptr<C>& mayaBuffer, Digest digest)
    {
        
        size_t size = MayaBufferSizeHelper(mayaBuffer.get());
       
        
        
        
        boost::shared_ptr<Array<T> > ret;
        {
            tbb::mutex::scoped_lock lock(ArrayRegistry<T>::mutex());
            ret = ArrayRegistry<T>::lookupNonReadable(digest, size);
        
            if (!ret) {
                ret = boost::make_shared<MayaBufferArray<T, C> >(
                    mayaBuffer, digest);
                ArrayRegistry<T>::insert(ret);
            }
        }
        return ret;
    }
    virtual ~MayaBufferArray() {}
    virtual boost::shared_ptr<const ArrayReadInterface<T> > getReadable() const
    {
        
        
        
        boost::shared_ptr<const TempCopyReadableInterface> ret(boost::make_shared<const TempCopyReadableInterface>(GetTempArrayCopy()));
        return ret;
    }
    virtual boost::shared_ptr<ReadableArray<T> > getReadableArray() const
    {
        
        
        
        {
            
            tbb::mutex::scoped_lock lock(ArrayRegistry<T>::mutex());
            boost::shared_ptr<ReadableArray<T> > ret;
            
            
            ret = ArrayRegistry<T>::lookupReadable(this->digest(), this->bytes());
            if (ret)
                return ret;
        }
        
        boost::shared_array<T> rawData(GetTempArrayCopy());
        return SharedArray<T>::create(rawData, this->digest(), this->bytes()/sizeof(T));
    }
    boost::shared_ptr<C> getMBuffer() const
    {
        return fMayaBuffer;
    }
private:
    MayaBufferArray(const boost::shared_ptr<C>& mayaBuffer, Digest digest)  
        : Array<T>(MayaBufferSizeHelper(mayaBuffer.get()), digest, false)
        , fMayaBuffer(mayaBuffer)
    {}
    MayaBufferArray(const MayaBufferArray& other);  
    boost::shared_array<T> GetTempArrayCopy() const
    {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        assert(gsMainThreadId == tbb::this_tbb_thread::get_id());
        if (gsMainThreadId != tbb::this_tbb_thread::get_id())
            return boost::shared_array<T>();
        const T* src = (const T *)fMayaBuffer->map();
        size_t numBytes = this->bytes();
        size_t numValues = numBytes / sizeof(T);
        boost::shared_array<T> rawData(new T[numValues]);
        memcpy(rawData.get(), src, numBytes);
        fMayaBuffer->unmap();
        return rawData;
    }
    
    GPUCACHE_DECLARE_MAKE_SHARED_FRIEND_2;
    const boost::shared_ptr<C> fMayaBuffer;
};
template class MayaBufferArray<unsigned int, MHWRender::MIndexBuffer>;
template class MayaBufferArray<float, MHWRender::MVertexBuffer>;
typedef MayaBufferArray<unsigned int, MHWRender::MIndexBuffer> MayaIndexBufferWrapper;
typedef MayaBufferArray<float, MHWRender::MVertexBuffer> MayaVertexBufferWrapper;
class BuffersCache : boost::noncopyable
{
public:
    static BuffersCache& getInstance()
    {
        
        static BuffersCache sSingleton;
        return sSingleton;
    }
    
    
    
    void setBuffers(
        SubSceneOverride&                            subSceneOverride,
        const boost::shared_ptr<const IndexBuffer>&  indices,
        const boost::shared_ptr<const VertexBuffer>& positions,
        const boost::shared_ptr<const VertexBuffer>& normals,
        const boost::shared_ptr<const VertexBuffer>& uvs,
    )
    {
        assert(positions);
        if (!positions) return;
        
        if (!renderItem) {
            if (indices) {
                acquireIndexBuffer(indices);
            }
            acquireVertexBuffer(positions);
            if (normals) {
                acquireVertexBuffer(normals);
            }
            if (uvs) {
                acquireVertexBuffer(uvs);
            }
            return;
        }
        
        static const MString sPositions(
"positions");
 
        static const MString sNormals(
"normals");
 
        buffers.
addBuffer(sPositions, acquireVertexBuffer(positions));
        if (normals) {
            buffers.
addBuffer(sNormals, acquireVertexBuffer(normals));
        }
        if (uvs) {
            buffers.
addBuffer(sUVs, acquireVertexBuffer(uvs));
        }
        
        subSceneOverride.setGeometryForRenderItem(
            *renderItem,
            buffers,
            indices ? *acquireIndexBuffer(indices) : 
MIndexBuffer(MGeometry::kUnsignedInt32),
            &boundingBox
        );
    }
    
    
    void removeBuffers(
        const boost::shared_ptr<const IndexBuffer>&  indices,
        const boost::shared_ptr<const VertexBuffer>& positions,
        const boost::shared_ptr<const VertexBuffer>& normals = boost::shared_ptr<const VertexBuffer>(),
        const boost::shared_ptr<const VertexBuffer>& uvs     = boost::shared_ptr<const VertexBuffer>()
    )
    {
        if (indices) {
            removeBufferFromCache(indices);
        }
        if (positions) {
            removeBufferFromCache(positions);
        }
        if (normals) {
            removeBufferFromCache(normals);
        }
        if (uvs) {
            removeBufferFromCache(uvs);
        }
    }
    
    void updateBuffers(
        SubSceneOverride&                            subSceneOverride,
        const boost::shared_ptr<const IndexBuffer>&  indices,
        const boost::shared_ptr<const VertexBuffer>& positions,
        const boost::shared_ptr<const VertexBuffer>& normals,
        const boost::shared_ptr<const VertexBuffer>& uvs,
        const boost::shared_ptr<const IndexBuffer>&  prevIndices,
        const boost::shared_ptr<const VertexBuffer>& prevPositions,
        const boost::shared_ptr<const VertexBuffer>& prevNormals = boost::shared_ptr<const VertexBuffer>(),
        const boost::shared_ptr<const VertexBuffer>& prevUVs     = boost::shared_ptr<const VertexBuffer>()
    )
    {
        removeBuffers(prevIndices, prevPositions, prevNormals, prevUVs);
        setBuffers(subSceneOverride, renderItem, indices, positions, normals, uvs, boundingBox);
    }
    
    MIndexBuffer* lookup(
const boost::shared_ptr<const IndexBuffer>& indices)
 
    {
        lookup(indices, buffer);
        return buffer;
    }
    
    MVertexBuffer* lookup(
const boost::shared_ptr<const VertexBuffer>& vertices)
 
    {
        lookup(vertices, buffer);
        return buffer;
    }
    
    
    void shrink()
    {
        
        
        doDeleteQueuedBuffers();
        while (fTotalBufferSize > Config::maxVBOSize()) {
            
            
            if (fFreeBuffers.empty()) break;
            
            BufferSet::iterator it = fFreeBuffers.begin();
            fTotalBufferSize -= (*it).bytes();
            fFreeBuffers.erase(it);
        }
    }
    
    void clear()
    {
        fTotalBufferSize = 0;
        fActiveBuffers.clear();
        fFreeBuffers.clear();
        {
            tbb::mutex::scoped_lock lock(fBuffersToDeleteMutex);
            fBuffersToDelete.clear();
        }
    }
private:
    class BufferEntry;
    BuffersCache()
        : fTotalBufferSize(0)
    {
        ArrayBase::registerDestructionCallback(sArrayDestructionCb);
    }
    ~BuffersCache()
    {
        ArrayBase::unregisterDestructionCallback(sArrayDestructionCb);
    }
    
    
    MIndexBuffer* acquireIndexBuffer(
const boost::shared_ptr<const IndexBuffer>& indices)
 
    {
        assert(indices);
        addBufferToCache(indices).getBuffer(buffer);
        return buffer;
    }
    
    
    MVertexBuffer* acquireVertexBuffer(
const boost::shared_ptr<const VertexBuffer>& vertices)
 
    {
        assert(vertices);
        addBufferToCache(vertices).getBuffer(buffer);
        return buffer;
    }
    
    
    template<typename T>
    const BufferEntry& addBufferToCache(const T& buffer)
    {
        
        BufferSet::iterator it = fActiveBuffers.find(buffer);
        if (it != fActiveBuffers.end()) {
            
            
            assert((*it).refCount() > 0);
            (*it).ref();
            return *it;
        }
        
        it = fFreeBuffers.find(buffer);
        if (it != fFreeBuffers.end()) {
            
            
            assert((*it).refCount() == 0);
            BufferSet::iterator newIt = fActiveBuffers.insert(*it).first;
            fFreeBuffers.erase(it);
            (*newIt).ref();
            return *newIt;
        }
        
        
        BufferSet::iterator newIt = fActiveBuffers.insert(buffer).first;
        (*newIt).ref();
        fTotalBufferSize += (*newIt).bytes();
        return *newIt;
    }
    
    
    
    
    template<typename T>
    void removeBufferFromCache(const T& buffer)
    {
        
        BufferSet::iterator it = fActiveBuffers.find(buffer);
        assert(fFreeBuffers.find(buffer) == fFreeBuffers.end());
        if (it != fActiveBuffers.end()) {
            assert((*it).refCount() > 0);
            (*it).unref();
            
            
            if ((*it).refCount() == 0) {
                fFreeBuffers.insert(*it);
                fActiveBuffers.erase(it);
            }
        }
    }
    
    
    template<typename T, typename R>
    void lookup(const T& buffer, R& pointer)
    {
        
        BufferSet::iterator it = fActiveBuffers.find(buffer);
        if (it != fActiveBuffers.end()) {
            assert((*it).refCount() > 0);
            (*it).getBuffer(pointer);
            return;
        }
        
        it = fFreeBuffers.find(buffer);
        if (it != fFreeBuffers.end()) {
            assert((*it).refCount() == 0);
            (*it).getBuffer(pointer);
            return;
        }
        pointer = NULL;
    }
    
    
    void queueBufferForDelete(const ArrayBase::Key& key)
    {
        tbb::mutex::scoped_lock lock(fBuffersToDeleteMutex);
        fBuffersToDelete.insert(key);
    }
    
    void doDeleteQueuedBuffers()
    {
        if (!fBuffersToDelete.empty()) {
            tbb::mutex::scoped_lock lock(fBuffersToDeleteMutex);
            typedef BufferSet::nth_index<1>::type::iterator KeyIterator;
            BOOST_FOREACH (const ArrayBase::Key& key, fBuffersToDelete) {
                
                
                std::pair<KeyIterator,KeyIterator> range = 
                    fFreeBuffers.get<1>().equal_range(key);
                for (KeyIterator it = range.first; it != range.second; it++) {
                    assert((*it).refCount() == 0);
                    fTotalBufferSize -= (*it).bytes();
                }
                fFreeBuffers.get<1>().erase(key);
            }
            
            fBuffersToDelete.clear();
        }
    }
    static void sArrayDestructionCb(const ArrayBase::Key& key)
    {
        
        BuffersCache::getInstance().queueBufferForDelete(key);
        
        if (tbb::this_tbb_thread::get_id() == gsMainThreadId) {
            BuffersCache::getInstance().doDeleteQueuedBuffers();
        }
    }
    
    class BufferEntry
    {
    public:
        
        struct BufferKey
        {
            enum BufferType { kIndex, kVertex };
            BufferType          type;
            ArrayBase::Key      arrayKey;
            MGeometry::DataType dataType;
            MGeometry::Semantic semantic;
            BufferKey(const boost::shared_ptr<const IndexBuffer>& indices)
                : type(kIndex),
                  arrayKey(indices->array()->key()),
                  dataType(MGeometry::kUnsignedInt32),
                  semantic(MGeometry::kInvalidSemantic)
            {}
            BufferKey(const boost::shared_ptr<const VertexBuffer>& vertices)
                : type(kVertex),
                  arrayKey(vertices->array()->key()),
                  dataType(vertices->descriptor().dataType()),
                  semantic(vertices->descriptor().semantic())
            {}
        };
        struct BufferKeyHash : std::unary_function<BufferKey, std::size_t>
        {
            std::size_t operator()(const BufferKey& key) const
            {
                std::size_t hashCode = 0;
                boost::hash_combine(hashCode, key.type);
                boost::hash_combine(hashCode, ArrayBase::KeyHash()(key.arrayKey));
                boost::hash_combine(hashCode, key.dataType);
                boost::hash_combine(hashCode, key.semantic);
                return hashCode;
            }
        };
        struct BufferKeyEqualTo : std::binary_function<BufferKey, BufferKey, bool>
        {
            bool operator()(const BufferKey& x, const BufferKey& y) const
            {
                return x.type == y.type &&
                        ArrayBase::KeyEqualTo()(x.arrayKey, y.arrayKey) &&
                        x.dataType == y.dataType &&
                        x.semantic == y.semantic;
            }
        };
        BufferEntry(const boost::shared_ptr<const IndexBuffer>& indices)
            : fKey(indices),
              fRefCount(0)
        {
            
            if (indices->numIndices() > 0) {
                if (!indices->array()->isReadable())
                {
                    
                    
                    
                    const MayaIndexBufferWrapper* mbufferWrapper = dynamic_cast<const MayaIndexBufferWrapper*>(indices->array().get());
                    assert(mbufferWrapper);
                    if (mbufferWrapper) {
                        boost::shared_ptr<MIndexBuffer> mbuffer = mbufferWrapper->getMBuffer();
                        assert(mbuffer);
                        if (mbuffer) {
                            fIndexBuffer = mbuffer;
                            return;
                        }
                    }
                }
                fIndexBuffer.reset(
new MIndexBuffer(MGeometry::kUnsignedInt32));
                {
                    IndexBuffer::ReadInterfacePtr readable = indices->readableInterface();
                    const IndexBuffer::index_t* data = readable->get();
                    fIndexBuffer->update(data, 0, (unsigned int)indices->numIndices(), true);
                }
                
                
                
                
                
                
                if (indices->array()->isReadable()) {
                    boost::shared_ptr<Array<IndexBuffer::index_t> > mayaArray = MayaIndexBufferWrapper::create(fIndexBuffer, indices->array()->digest());
                    indices->ReplaceArrayInstance(mayaArray);
                }
            }
        }
        BufferEntry(const boost::shared_ptr<const VertexBuffer>& vertices)
            : fKey(vertices),
              fRefCount(0)
        {
            
            if (vertices->numVerts() > 0) {
                
                assert(fKey.dataType == MGeometry::kFloat);
                bool allowReplaceBufferArray = true;
                if (!vertices->array()->isReadable())
                {
                    
                    
                    
                    
                    const MayaVertexBufferWrapper* mbufferWrapper = dynamic_cast<const MayaVertexBufferWrapper*>(vertices->array().get());
                    assert(mbufferWrapper);
                    if (mbufferWrapper) {
                        boost::shared_ptr<MVertexBuffer> mbuffer = mbufferWrapper->getMBuffer();
                        assert(mbuffer);
                        if (mbuffer) {
                            if (mbuffer->descriptor().semantic() == vertices->descriptor().semantic()) {
                                
                                fVertexBuffer = mbuffer;
                                return;
                            } else {
                                
                                
                                
                                
                                
                                boost::shared_ptr<Array<float> > softwareArray = vertices->array()->getReadableArray();
                                vertices->ReplaceArrayInstance(softwareArray);
                                allowReplaceBufferArray = false;
                                
                                
                            }
                        }
                    }
                }
                {
                    VertexBuffer::ReadInterfacePtr readable = vertices->readableInterface();
                    const float* data = readable->get();
                    fVertexBuffer->update(data, 0, (unsigned int)vertices->numVerts(), true);
                }
                
                
                
                
                
                
                
                if (allowReplaceBufferArray && vertices->array()->isReadable()) {
                    boost::shared_ptr<Array<float> > mayaArray = MayaVertexBufferWrapper::create(fVertexBuffer, vertices->array()->digest());
                    vertices->ReplaceArrayInstance(mayaArray);
                }
            }
        }
        
        const BufferKey&      key() const      { return fKey; }
        
        const ArrayBase::Key& arrayKey() const { return fKey.arrayKey; }
        
        size_t bytes() const    { return fKey.arrayKey.fBytes; }
        
        {
            assert(fIndexBuffer);
            buffer = fIndexBuffer.get();
        }
        
        {
            assert(fVertexBuffer);
            buffer = fVertexBuffer.get();
        }
        void   ref() const      { fRefCount++; }
        void   unref() const    { fRefCount--; }
        size_t refCount() const { return fRefCount; }
        
    private:
        BufferKey                           fKey;
        boost::shared_ptr<MIndexBuffer>     fIndexBuffer;
        boost::shared_ptr<MVertexBuffer>    fVertexBuffer;
        mutable size_t                      fRefCount;
    };
    typedef boost::multi_index_container<
        BufferEntry,
        boost::multi_index::indexed_by<
            
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_CONST_MEM_FUN(BufferEntry,const BufferEntry::BufferKey&,key),
                BufferEntry::BufferKeyHash,
                BufferEntry::BufferKeyEqualTo
            >,
            
            
            boost::multi_index::hashed_non_unique<
                BOOST_MULTI_INDEX_CONST_MEM_FUN(BufferEntry,const ArrayBase::Key&,arrayKey),
                ArrayBase::KeyHash,
                ArrayBase::KeyEqualTo
            >
        >
    > BufferSet;
    typedef boost::unordered_set<
        ArrayBase::Key,
        ArrayBase::KeyHash,
        ArrayBase::KeyEqualTo
    > KeySet;
    size_t          fTotalBufferSize;
    BufferSet       fActiveBuffers;
    BufferSet       fFreeBuffers;
    tbb::mutex      fBuffersToDeleteMutex;
    KeySet          fBuffersToDelete;
};
{
public:
    SubNodeUserData(const SubNode& subNode)
          fSubNode(subNode)
    {}
    virtual ~SubNodeUserData()
    {}
    void hintShapeReadOrder() const
    {
        
        
        GlobalReaderCache::theCache().hintShapeReadOrder(fSubNode);
    }
private:
    const SubNode& fSubNode;
};
static void SetDashLinePattern(
MShaderInstance* shader, 
unsigned short pattern)
 
{
    static const MString sDashPattern = 
"dashPattern";
 
    unsigned short newPattern = pattern;
    if (newPattern != 0) {
        while ((newPattern & 0x8000) == 0) {
            newPattern <<= 1;
        }
    }
}
void BoundingBoxPlaceHolderDrawCallback(
MDrawContext& context,
 
{
    int numRenderItems = renderItemList.
length();
 
    for (int i = 0; i < numRenderItems; i++) {
        if (renderItem) {
            SubNodeUserData* userData =
                dynamic_cast<SubNodeUserData*
>(renderItem->
customData());
 
            if (userData) {
                userData->hintShapeReadOrder();
            }
        }
    }
}
{
    
    const DisplayPref::WireframeOnShadedMode wireOnShadedMode =
        DisplayPref::wireframeOnShadedMode();
    
    if (wireOnShadedMode == DisplayPref::kWireframeOnShadedFull) {
        assert(0);  
        return;
    }
    
    if (displayStyle & (MDrawContext::kGouraudShaded | MDrawContext::kTextured)) {
        const unsigned short pattern =
            (wireOnShadedMode == DisplayPref::kWireframeOnShadedReduced)
            ? Config::kLineStippleDotted  
            : 0;                          
        static const MString sDashPattern = 
"dashPattern";        
 
        SetDashLinePattern(shader, pattern);
    }
}
{
    
    const DisplayPref::WireframeOnShadedMode wireOnShadedMode =
        DisplayPref::wireframeOnShadedMode();
    
    if (wireOnShadedMode == DisplayPref::kWireframeOnShadedFull) {
        assert(0);  
        return;
    }
    
    SetDashLinePattern(shader, Config::kLineStippleShortDashed);
}
{
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return NULL;
    if (!shaderMgr) return NULL;
}
{
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return NULL;
    if (!shaderMgr) return NULL;
}
{
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return NULL;
    if (!shaderMgr) return NULL;
        WireframePreDrawCallback, WireframePostDrawCallback);
}
{
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return NULL;
    if (!shaderMgr) return NULL;
        NULL, BoundingBoxPlaceHolderDrawCallback);
}
{
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return NULL;
    if (!shaderMgr) return NULL;
}
{
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return;
    if (!shaderMgr) return;
    if (shader) {
        shader = NULL;
    }
}
{
    if (shader) {
        
        const float color[3] = {diffuseColor.
r, diffuseColor.
g, diffuseColor.
b};
 
        
        if (diffuseColor.
a < 1.0f) {
 
            const float oneMinusAlpha =
                (diffuseColor.
a >= 0.0f) ? 1.0f - diffuseColor.
a : 1.0f;
            const float transparency[3] = {oneMinusAlpha, oneMinusAlpha, oneMinusAlpha};
        }
        else {
        }
        
    }
}
bool useHardwareInstancing()
{
    
    
    static MPlug sHwInstancingPlug;
 
    if (sHwInstancingPlug.
isNull()) {
 
        sl.
add(
"hardwareRenderingGlobals.hwInstancing");
        MStatAssert(stat);
    }
    return sHwInstancingPlug.
asBool() && Config::useHardwareInstancing();
 
}
class ShaderInstancePtr
{
public:
    
    ShaderInstancePtr()
    {}
    
    ShaderInstancePtr(boost::shared_ptr<MShaderInstance> shader,
                      boost::shared_ptr<MShaderInstance> source)
        : fShader(shader), fTemplate(source)
    {}
    ~ShaderInstancePtr()
    {}
    operator bool () const
    {
        return fShader && fTemplate;
    }
    {
        assert(fShader);
        return fShader.get();
    }
    {
        assert(fShader);
        return fShader.get();
    }
    boost::shared_ptr<MShaderInstance> getShader() const
    {
        assert(fShader);
        return fShader;
    }
    boost::shared_ptr<MShaderInstance> getTemplate() const
    {
        assert(fTemplate);
        return fTemplate;
    }
    void reset()
    {
        fShader.reset();
        fTemplate.reset();
    }
    bool operator==(const ShaderInstancePtr& rv) const
    {
        return fShader == rv.fShader && fTemplate == rv.fTemplate;
    }
    bool operator!=(const ShaderInstancePtr& rv) const
    {
        return !(operator==(rv));
    }
private:
    boost::shared_ptr<MShaderInstance> fShader;
    boost::shared_ptr<MShaderInstance> fTemplate;
};
class ShaderTemplatePtr
{
public:
    
    ShaderTemplatePtr()
    {}
    
    ShaderTemplatePtr(boost::shared_ptr<MShaderInstance> source)
        : fTemplate(source)
    {}
    ~ShaderTemplatePtr()
    {}
    operator bool () const
    {
        return (fTemplate.get() != NULL);
    }
    {
        assert(fTemplate);
        return fTemplate.get();
    }
    boost::shared_ptr<MShaderInstance> getTemplate() const
    {
        assert(fTemplate);
        return fTemplate;
    }
    ShaderInstancePtr newShaderInstance(Deleter deleter) const
    {
        assert(fTemplate);
        boost::shared_ptr<MShaderInstance> newShader;
        newShader.reset(fTemplate->clone(), std::ptr_fun(deleter));
        return ShaderInstancePtr(newShader, fTemplate);
    }
private:
    boost::shared_ptr<MShaderInstance> fTemplate;
};
class ShaderCache : boost::noncopyable
{
public:
    static ShaderCache& getInstance()
    {
        
        static ShaderCache sSingleton;
        return sSingleton;
    }
    ShaderInstancePtr newPointShader(Deleter deleter)
    {
        
        MString key = 
"_reserved_point_shader_";
 
        FragmentAndShaderTemplateCache::nth_index<0>::type::iterator it =
            fFragmentCache.get<0>().find(key);
        
        if (it != fFragmentCache.get<0>().end()) {
            ShaderTemplatePtr templateShader = it->ptr.lock();
            assert(templateShader);  
            return templateShader.newShaderInstance(deleter);
        }
        
        ShaderTemplatePtr templateShader =
            wrapShaderTemplate(getPointShaderInstance());
        if (templateShader) {
            
            FragmentAndShaderTemplate entry;
            entry.fragmentAndOutput = key;
            entry.shader            = templateShader.get();
            entry.ptr               = templateShader.getTemplate();
            fFragmentCache.insert(entry);
            return templateShader.newShaderInstance(deleter);
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr newWireShader(Deleter deleter)
    {
        
        MString key = 
"_reserved_wire_shader_";
 
        FragmentAndShaderTemplateCache::nth_index<0>::type::iterator it =
            fFragmentCache.get<0>().find(key);
        
        if (it != fFragmentCache.get<0>().end()) {
            ShaderTemplatePtr templateShader = it->ptr.lock();
            assert(templateShader);  
            return templateShader.newShaderInstance(deleter);
        }
        
        ShaderTemplatePtr templateShader =
            wrapShaderTemplate(getWireShaderInstance());
        if (templateShader) {
            
            FragmentAndShaderTemplate entry;
            entry.fragmentAndOutput = key;
            entry.shader            = templateShader.get();
            entry.ptr               = templateShader.getTemplate();
            fFragmentCache.insert(entry);
            return templateShader.newShaderInstance(deleter);
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr newWireShaderWithCB(Deleter deleter)
    {
        
        MString key = 
"_reserved_wire_shader_with_cb_";
 
        FragmentAndShaderTemplateCache::nth_index<0>::type::iterator it =
            fFragmentCache.get<0>().find(key);
        
        if (it != fFragmentCache.get<0>().end()) {
            ShaderTemplatePtr templateShader = it->ptr.lock();
            assert(templateShader);  
            return templateShader.newShaderInstance(deleter);
        }
        
        ShaderTemplatePtr templateShader =
            wrapShaderTemplate(getWireShaderInstanceWithCB());
        if (templateShader) {
            
            FragmentAndShaderTemplate entry;
            entry.fragmentAndOutput = key;
            entry.shader            = templateShader.get();
            entry.ptr               = templateShader.getTemplate();
            fFragmentCache.insert(entry);
            return templateShader.newShaderInstance(deleter);
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr newBoundingBoxPlaceHolderShader(Deleter deleter)
    {
        
        MString key = 
"_reserved_bounding_box_place_holder_shader_";
 
        FragmentAndShaderTemplateCache::nth_index<0>::type::iterator it =
            fFragmentCache.get<0>().find(key);
        
        if (it != fFragmentCache.get<0>().end()) {
            ShaderTemplatePtr templateShader = it->ptr.lock();
            assert(templateShader);  
            return templateShader.newShaderInstance(deleter);
        }
        
        ShaderTemplatePtr templateShader =
            wrapShaderTemplate(getBoundingBoxPlaceHolderShaderInstance());
        if (templateShader) {
            
            FragmentAndShaderTemplate entry;
            entry.fragmentAndOutput = key;
            entry.shader            = templateShader.get();
            entry.ptr               = templateShader.getTemplate();
            fFragmentCache.insert(entry);
            return templateShader.newShaderInstance(deleter);
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr newDiffuseColorShader(Deleter deleter)
    {
        
        MString key = 
"_reserved_diffuse_color_shader_";
 
        FragmentAndShaderTemplateCache::nth_index<0>::type::iterator it =
            fFragmentCache.get<0>().find(key);
        
        if (it != fFragmentCache.get<0>().end()) {
            ShaderTemplatePtr templateShader = it->ptr.lock();
            assert(templateShader);  
            return templateShader.newShaderInstance(deleter);
        }
        
        ShaderTemplatePtr templateShader =
            wrapShaderTemplate(getDiffuseColorShaderInstance());
        if (templateShader) {
            
            FragmentAndShaderTemplate entry;
            entry.fragmentAndOutput = key;
            entry.shader            = templateShader.get();
            entry.ptr               = templateShader.getTemplate();
            fFragmentCache.insert(entry);
            return templateShader.newShaderInstance(deleter);
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr newFragmentShader(
const MString& fragmentName,
                                        Deleter        deleter)
    {
        
        MString key = fragmentName + 
":" + outputStructName;
 
        FragmentAndShaderTemplateCache::nth_index<0>::type::iterator it =
            fFragmentCache.get<0>().find(key);
        
        if (it != fFragmentCache.get<0>().end()) {
            ShaderTemplatePtr templateShader = it->ptr.lock();
            assert(templateShader);  
            return templateShader.newShaderInstance(deleter);
        }
        
        ShaderTemplatePtr templateShader =
            createFragmentShader(fragmentName, outputStructName);
        if (templateShader) {
            
            FragmentAndShaderTemplate entry;
            entry.fragmentAndOutput = key;
            entry.shader            = templateShader.get();
            entry.ptr               = templateShader.getTemplate();
            fFragmentCache.insert(entry);
            return templateShader.newShaderInstance(deleter);
        }
        assert(0);
        return ShaderInstancePtr();
    }
private:
    ShaderCache()  {}
    ~ShaderCache() {}
    
    {
        assert(shader);
        getInstance().removeShaderTemplateFromCache(shader);
        releaseShaderInstance(shader);
    }
    
    {
        assert(shader);
        if (!shader) return;
        
        fFragmentCache.get<1>().erase(shader);
    }
    
    {
        assert(shader);
        if (!shader) return ShaderTemplatePtr();
        boost::shared_ptr<MShaderInstance> ptr;
        ptr.reset(shader, std::ptr_fun(shaderTemplateDeleter));
        return ShaderTemplatePtr(ptr);
    }
    
    ShaderTemplatePtr createFragmentShader(
const MString& fragmentName,
    {
        MRenderer* renderer = MRenderer::theRenderer();
 
        if (!renderer) return ShaderTemplatePtr();
        if (!shaderMgr) return ShaderTemplatePtr();
        return wrapShaderTemplate(
    }
private:
    struct FragmentAndShaderTemplate {
        boost::weak_ptr<MShaderInstance> ptr;
    };
    typedef boost::multi_index_container<
        FragmentAndShaderTemplate,
        boost::multi_index::indexed_by<
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_MEMBER(FragmentAndShaderTemplate,
MString,fragmentAndOutput),
                MStringHash
            >,
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_MEMBER(FragmentAndShaderTemplate,
MShaderInstance*,shader)
            >
        >
    > FragmentAndShaderTemplateCache;
    FragmentAndShaderTemplateCache  fFragmentCache;
};
class MaterialGraphTranslatorShaded : public ConcreteMaterialNodeVisitor
{
public:
    
    MaterialGraphTranslatorShaded(Deleter deleter, double timeInSeconds)
        : fShader(), fDeleter(deleter), fTimeInSeconds(timeInSeconds)
    {}
    
    MaterialGraphTranslatorShaded(ShaderInstancePtr& shader, double timeInSeconds)
        : fShader(shader), fDeleter(NULL), fTimeInSeconds(timeInSeconds)
    {}
    virtual ~MaterialGraphTranslatorShaded() {}
    ShaderInstancePtr getShader() const
    { return fShader; }
    virtual void visit(const LambertMaterial& node)
    {
        if (!fShader) {
            createShader("mayaLambertSurface", "outSurfaceFinal");
        }
        setupLambert(node);
    }
    virtual void visit(const PhongMaterial& node)
    {
        if (!fShader) {
            createShader("mayaPhongSurface", "outSurfaceFinal");
        }
        setupPhong(node);
        setupLambert(node);
    }
    virtual void visit(const BlinnMaterial& node)
    {
        if (!fShader) {
            createShader("mayaBlinnSurface", "outSurfaceFinal");
        }
        setupBlinn(node);
        setupLambert(node);
    }
    
    virtual void visit(const SurfaceMaterial& node) {}
    virtual void visit(const Texture2d& node) {}
    virtual void visit(const FileTexture& node) {}
private:
    void createShader(
const MString& fragmentName,
 
    {
        assert(fDeleter);
        fShader = ShaderCache::getInstance().newFragmentShader(
            fragmentName, structOutputName, fDeleter);
        assert(fShader);
    }
    void setupLambert(const LambertMaterial& lambert)
    {
        if (!fShader) return;
        
        {
                ShadedModeColor::evaluateDefaultColor(lambert.Color, fTimeInSeconds);
            const float buffer[3] = {color.
r, color.
g, color.
b};
 
            fShader->setParameter("color", buffer);
        }
        
        {
                ShadedModeColor::evaluateColor(lambert.Transparency, fTimeInSeconds);
            const float buffer[3] = {transparency.
r, transparency.
g, transparency.
b};
 
            fShader->setParameter("transparency", buffer);
            if (transparency.
r > 0 || transparency.
g > 0 || transparency.
b > 0) {
 
                fShader->setIsTransparent(true);
            }
            else {
                fShader->setIsTransparent(false);
            }
        }
        
        {
                ShadedModeColor::evaluateColor(lambert.AmbientColor, fTimeInSeconds);
            const float buffer[3] = {ambientColor.
r, ambientColor.
g, ambientColor.
b};
 
            fShader->setParameter("ambientColor", buffer);
        }
        
        {
                ShadedModeColor::evaluateColor(lambert.Incandescence, fTimeInSeconds);
            const float buffer[3] = {incandescence.
r, incandescence.
g, incandescence.
b};
 
            fShader->setParameter("incandescence", buffer);
        }
        
        {
            const float diffuse =
                ShadedModeColor::evaluateFloat(lambert.Diffuse, fTimeInSeconds);
            fShader->setParameter("diffuse", diffuse);
        }
        
        {
            const float translucence =
                ShadedModeColor::evaluateFloat(lambert.Translucence, fTimeInSeconds);
            fShader->setParameter("translucence", translucence);
        }
        
        {
            const float translucenceDepth =
                ShadedModeColor::evaluateFloat(lambert.TranslucenceDepth, fTimeInSeconds);
            fShader->setParameter("translucenceDepth", translucenceDepth);
        }
        
        {
            const float translucenceFocus =
                ShadedModeColor::evaluateFloat(lambert.TranslucenceFocus, fTimeInSeconds);
            fShader->setParameter("translucenceFocus", translucenceFocus);
        }
        
        {
            const bool hideSource =
                ShadedModeColor::evaluateBool(lambert.HideSource, fTimeInSeconds);
            fShader->setParameter("hideSource", hideSource);
        }
        
        {
            const float glowIntensity =
                ShadedModeColor::evaluateFloat(lambert.GlowIntensity, fTimeInSeconds);
            fShader->setParameter("glowIntensity", glowIntensity);
        }
    }
    void setupPhong(const PhongMaterial& phong)
    {
        if (!fShader) return;
        
        {
            const float cosinePower =
                ShadedModeColor::evaluateFloat(phong.CosinePower, fTimeInSeconds);
            fShader->setParameter("cosinePower", cosinePower);
        }
        
        {
                ShadedModeColor::evaluateColor(phong.SpecularColor, fTimeInSeconds);
            const float buffer[3] = {specularColor.
r, specularColor.
g, specularColor.
b};
 
            fShader->setParameter("specularColor", buffer);
        }
        
        {
            const float reflectivity =
                ShadedModeColor::evaluateFloat(phong.Reflectivity, fTimeInSeconds);
            fShader->setParameter("reflectivity", reflectivity);
        }
        
        {
                ShadedModeColor::evaluateColor(phong.ReflectedColor, fTimeInSeconds);
            const float buffer[3] = {reflectedColor.
r, reflectedColor.
g, reflectedColor.
b};
 
            fShader->setParameter("reflectedColor", buffer);
        }
    }
    void setupBlinn(const BlinnMaterial& blinn)
    {
        if (!fShader) return;
        
        {
            const float eccentricity =
                ShadedModeColor::evaluateFloat(blinn.Eccentricity, fTimeInSeconds);
            fShader->setParameter("eccentricity", eccentricity);
        }
        
        {
            const float specularRollOff =
                ShadedModeColor::evaluateFloat(blinn.SpecularRollOff, fTimeInSeconds);
            fShader->setParameter("specularRollOff", specularRollOff);
        }
        
        {
                ShadedModeColor::evaluateColor(blinn.SpecularColor, fTimeInSeconds);
            const float buffer[3] = {specularColor.
r, specularColor.
g, specularColor.
b};
 
            fShader->setParameter("specularColor", buffer);
        }
        
        {
            const float reflectivity =
                ShadedModeColor::evaluateFloat(blinn.Reflectivity, fTimeInSeconds);
            fShader->setParameter("reflectivity", reflectivity);
        }
        
        {
                ShadedModeColor::evaluateColor(blinn.ReflectedColor, fTimeInSeconds);
            const float buffer[3] = {reflectedColor.
r, reflectedColor.
g, reflectedColor.
b};
 
            fShader->setParameter("reflectedColor", buffer);
        }
    }
    ShaderInstancePtr fShader;
    Deleter           fDeleter;
    const double      fTimeInSeconds;
};
class ShaderInstanceCache : boost::noncopyable
{
public:
    static ShaderInstanceCache& getInstance()
    {
        
        static ShaderInstanceCache sSingleton;
        return sSingleton;
    }
    ShaderInstancePtr getSharedPointShader()
    {
        
        
        ColorAndShaderInstanceCache::nth_index<0>::type::iterator it =
            fPointShaders.get<0>().find(color);
        
        if (it != fPointShaders.get<0>().end()) {
            boost::shared_ptr<MShaderInstance> shader = it->ptr.lock();
            assert(shader);  
            return ShaderInstancePtr(shader, it->source);
        }
        
        ShaderInstancePtr shader =
            ShaderCache::getInstance().newPointShader(shaderInstanceDeleter);
        if (shader) {
            
            const float solidColor[4] = {color.
r, color.
g, color.
b, 1.0f};
 
            shader->setParameter("solidColor", solidColor);
            
            ColorAndShaderInstance entry;
            entry.color  = color;
            entry.shader = shader.get();
            entry.ptr    = shader.getShader();
            entry.source = shader.getTemplate();
            fPointShaders.insert(entry);
            return shader;
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr getSharedWireShader(
const MColor& color)
    {
        
        ColorAndShaderInstanceCache::nth_index<0>::type::iterator it =
            fWireShaders.get<0>().find(color);
        
        if (it != fWireShaders.get<0>().end()) {
            boost::shared_ptr<MShaderInstance> shader = it->ptr.lock();
            assert(shader);  
            return ShaderInstancePtr(shader, it->source);
        }
        
        ShaderInstancePtr shader =
            ShaderCache::getInstance().newWireShader(shaderInstanceDeleter);
        if (shader) {
            
           SetDashLinePattern(shader.get(), Config::kLineStippleShortDashed);
            
            const float solidColor[4] = {color.
r, color.
g, color.
b, 1.0f};
 
            shader->setParameter("solidColor", solidColor);
            
            ColorAndShaderInstance entry;
            entry.color  = color;
            entry.shader = shader.get();
            entry.ptr    = shader.getShader();
            entry.source = shader.getTemplate();
            fWireShaders.insert(entry);
            return shader;
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr getSharedWireShaderWithCB(
const MColor& color)
    {
        
        ColorAndShaderInstanceCache::nth_index<0>::type::iterator it =
            fWireShadersWithCB.get<0>().find(color);
        
        if (it != fWireShadersWithCB.get<0>().end()) {
            boost::shared_ptr<MShaderInstance> shader = it->ptr.lock();
            assert(shader);  
            return ShaderInstancePtr(shader, it->source);
        }
        
        ShaderInstancePtr shader =
            ShaderCache::getInstance().newWireShaderWithCB(shaderInstanceDeleter);
        if (shader) {
            
            SetDashLinePattern(shader.get(), Config::kLineStippleShortDashed);
            
            const float solidColor[4] = {color.
r, color.
g, color.
b, 1.0f};
 
            shader->setParameter("solidColor", solidColor);
            
            ColorAndShaderInstance entry;
            entry.color  = color;
            entry.shader = shader.get();
            entry.ptr    = shader.getShader();
            entry.source = shader.getTemplate();
            fWireShadersWithCB.insert(entry);
            return shader;
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr getSharedBoundingBoxPlaceHolderShader(
const MColor& color)
    {
        
        ColorAndShaderInstanceCache::nth_index<0>::type::iterator it =
            fBoundingBoxPlaceHolderShaders.get<0>().find(color);
        
        if (it != fBoundingBoxPlaceHolderShaders.get<0>().end()) {
            boost::shared_ptr<MShaderInstance> shader = it->ptr.lock();
            assert(shader);  
            return ShaderInstancePtr(shader, it->source);
        }
        
        ShaderInstancePtr shader =
            ShaderCache::getInstance().newBoundingBoxPlaceHolderShader(shaderInstanceDeleter);
        if (shader) {
            
            SetDashLinePattern(shader.get(), Config::kLineStippleShortDashed);
            
            const float solidColor[4] = {color.
r, color.
g, color.
b, 1.0f};
 
            shader->setParameter("solidColor", solidColor);
            
            ColorAndShaderInstance entry;
            entry.color  = color;
            entry.shader = shader.get();
            entry.ptr    = shader.getShader();
            entry.source = shader.getTemplate();
            fBoundingBoxPlaceHolderShaders.insert(entry);
            return shader;
        }
        assert(0);
        return ShaderInstancePtr();
    }
    ShaderInstancePtr getSharedDiffuseColorShader(
const MColor& color)
    {
        
        ColorAndShaderInstanceCache::nth_index<0>::type::iterator it =
            fDiffuseColorShaders.get<0>().find(color);
        
        if (it != fDiffuseColorShaders.get<0>().end()) {
            boost::shared_ptr<MShaderInstance> shader = it->ptr.lock();
            assert(shader);  
            return ShaderInstancePtr(shader, it->source);
        }
        
        ShaderInstancePtr shader =
            ShaderCache::getInstance().newDiffuseColorShader(shaderInstanceDeleter);
        if (shader) {
            
            setDiffuseColor(shader.get(), color);
            
            ColorAndShaderInstance entry;
            entry.color  = color;
            entry.shader = shader.get();
            entry.ptr    = shader.getShader();
            entry.source = shader.getTemplate();
            fDiffuseColorShaders.insert(entry);
            return shader;
        }
        assert(0);
        return ShaderInstancePtr();
    }
    
    
    ShaderInstancePtr getUniqueDiffuseColorShader(
const MColor& color)
    {
        ShaderInstancePtr shader =
            ShaderCache::getInstance().newDiffuseColorShader(shaderInstanceDeleter);
        if (shader) {
            
            setDiffuseColor(shader.get(), color);
            return shader;
        }
        return ShaderInstancePtr();
    }
    
    ShaderInstancePtr getSharedShadedMaterialShader(
        const MaterialGraph::Ptr& material,
        double                    timeInSeconds
    )
    {
        assert(material);
        if (!material) return ShaderInstancePtr();
        
        MaterialAndShaderInstanceCache::nth_index<0>::type::iterator it =
            fShadedMaterialShaders.get<0>().find(material);
        
        if (it != fShadedMaterialShaders.get<0>().end()) {
            boost::shared_ptr<MShaderInstance> shader = it->ptr.lock();
            assert(shader);  
            return ShaderInstancePtr(shader, it->source);
        }
        
        const MaterialNode::Ptr& rootNode = material->rootNode();
        assert(rootNode);
        ShaderInstancePtr shader;
        if (rootNode) {
            MaterialGraphTranslatorShaded shadedTranslator(shaderInstanceDeleter, timeInSeconds);
            rootNode->accept(shadedTranslator);
            shader = shadedTranslator.getShader();
        }
        if (shader) {
            
            MaterialAndShaderInstance entry;
            entry.material      = material;
            entry.shader        = shader.get();
            entry.ptr           = shader.getShader();
            entry.source        = shader.getTemplate();
            entry.isAnimated    = material->isAnimated();
            entry.timeInSeconds = timeInSeconds;
            fShadedMaterialShaders.insert(entry);
            return shader;
        }
        assert(0);
        return ShaderInstancePtr();
    }
    void updateCachedShadedShaders(double timeInSeconds)
    {
        
        BOOST_FOREACH (const MaterialAndShaderInstance& entry, fShadedMaterialShaders) {
            
            if (!entry.isAnimated) continue;
            
            if (entry.timeInSeconds == timeInSeconds) continue;
            
            const MaterialNode::Ptr& rootNode = entry.material->rootNode();
            if (rootNode) {
                ShaderInstancePtr shader(entry.ptr.lock(), entry.source);
                if (shader) {
                    MaterialGraphTranslatorShaded shadedTranslator(shader, timeInSeconds);
                    rootNode->accept(shadedTranslator);
                }
            }
            
            entry.timeInSeconds = timeInSeconds;
        }
    }
private:
    
    {
        assert(shader);
        getInstance().removeShaderInstanceFromCache(shader);
        releaseShaderInstance(shader);
    }
    
    {
        assert(shader);
        if (!shader) return;
        
        fPointShaders.get<1>().erase(shader);
        fWireShaders.get<1>().erase(shader);
        fWireShadersWithCB.get<1>().erase(shader);
        fBoundingBoxPlaceHolderShaders.get<1>().erase(shader);
        fDiffuseColorShaders.get<1>().erase(shader);
        fShadedMaterialShaders.get<1>().erase(shader);
    }
private:
    ShaderInstanceCache()  {}
    ~ShaderInstanceCache() {}
    
    struct MColorHash : std::unary_function<MColor, std::size_t>
    {
        std::size_t operator()(
const MColor& key)
 const        {
            std::size_t seed = 0;
            boost::hash_combine(seed, key.
r);
            boost::hash_combine(seed, key.
g);
            boost::hash_combine(seed, key.
b);
            boost::hash_combine(seed, key.
a);
            return seed;
        }
    };
    
    struct MaterialGraphHash : std::unary_function<MaterialGraph::Ptr, std::size_t>
    {
        std::size_t operator()(const MaterialGraph::Ptr& key) const
        {
            return boost::hash_value(key.get());
        }
    };
    
    struct ColorAndShaderInstance {
        boost::weak_ptr<MShaderInstance>   ptr;
        boost::shared_ptr<MShaderInstance> source;
    };
    typedef boost::multi_index_container<
        ColorAndShaderInstance,
        boost::multi_index::indexed_by<
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_MEMBER(ColorAndShaderInstance,
MColor,color),
                MColorHash
            >,
            boost::multi_index::hashed_unique<
            >
        >
    > ColorAndShaderInstanceCache;
    
    struct MaterialAndShaderInstance {
        MaterialGraph::Ptr                 material;
        boost::weak_ptr<MShaderInstance>   ptr;
        boost::shared_ptr<MShaderInstance> source;
        bool                               isAnimated;
        mutable double                     timeInSeconds;
    };
    typedef boost::multi_index_container<
        MaterialAndShaderInstance,
        boost::multi_index::indexed_by<
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_MEMBER(MaterialAndShaderInstance,MaterialGraph::Ptr,material),
                MaterialGraphHash
            >,
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_MEMBER(MaterialAndShaderInstance,
MShaderInstance*,shader)
            >
        >
    > MaterialAndShaderInstanceCache;
    ColorAndShaderInstanceCache    fPointShaders;
    ColorAndShaderInstanceCache    fWireShaders;
    ColorAndShaderInstanceCache    fWireShadersWithCB;
    ColorAndShaderInstanceCache    fBoundingBoxPlaceHolderShaders;
    ColorAndShaderInstanceCache    fDiffuseColorShaders;
    MaterialAndShaderInstanceCache fShadedMaterialShaders;
};
class RenderItemWrapper;
class HardwareInstanceManagerImpl;
class HardwareInstanceData : boost::noncopyable
{
public:
    HardwareInstanceData(HardwareInstanceManagerImpl* manager,
                         RenderItemWrapper*           renderItem)
        : fMasterData(NULL),
          fInstanceId(0),
          fRenderItem(renderItem),
          fManager(manager)
    {}
    ~HardwareInstanceData()
    {}
    
    HardwareInstanceData* masterData() const { return fMasterData; }
    
    unsigned int instanceId() const { return fInstanceId; }
    
    
    RenderItemWrapper* renderItem() const { return fRenderItem; }
    
    bool isInstanced() const  { return fInstanceId > 0; }
    
    bool isMasterItem() const { return fMasterData == this; }
    
    void setupCandidate(HardwareInstanceData* master)
    {
        assert(master);
        fMasterData = master;
        fInstanceId = 0;
    }
    
    void setupInstance(HardwareInstanceData* master, unsigned int instanceId)
    {
        assert(master);
        assert(instanceId > 0);
        fMasterData = master;
        fInstanceId = instanceId;
    }
    
    void clearInstanceData()
    {
        fMasterData = NULL;
        fInstanceId = 0;
    }
    
    
    void notifyInstancingChange();
    
    
    void notifyInstancingClear();
    
    void notifyWorldMatrixChange();
    
    void notifyDestroy();
private:
    HardwareInstanceData* fMasterData;
    unsigned int          fInstanceId;
    RenderItemWrapper*           fRenderItem;
    HardwareInstanceManagerImpl* fManager;
};
class RenderItemWrapper : boost::noncopyable
{
public:
    typedef boost::shared_ptr<RenderItemWrapper> Ptr;
    RenderItemWrapper(
const MString&                     name,
                      const MGeometry::Primitive         primitive)
        : fName(name),
          fType(type),
          fPrimitive(primitive),
          fRenderItem(NULL),
          fEnabled(true),
          fDepthPriority(
MRenderItem::sDormantFilledDepthPriority),
          fIsPointSnapping(false),
          fExcludedFromPostEffects(true),
          fCastsShadows(false),
          fReceivesShadows(false)
    {
        
        fRenderItem = MRenderItem::Create(
            name,
            type,
            primitive
        );
        assert(fRenderItem);
    }
    ~RenderItemWrapper()
    {
        
        BuffersCache::getInstance().removeBuffers(
            fIndices,
            fPositions,
            fNormals,
            fUVs
        );
        
        notifyDestroy();
    }
    {
        assert(fRenderItem);
        container.
add(fRenderItem);
    }
    {
        if (fRenderItem) {
            assert(fName == fRenderItem->name());
            fRenderItem->setCustomData(NULL);
            fRenderItem = NULL;
        }
    }
    void setEnabled(bool enabled)
    {
        if (fEnabled != enabled) {
            
            if (fRenderItem) {
                fRenderItem->enable(enabled);
            }
            
            fEnabled = enabled;
            
            if (fType == MRenderItem::MaterialSceneItem) {
                MRenderer::setLightsAndShadowsDirty();
            }
            notifyInstancingChange();
        }
    }
    void setWorldMatrix(
const MMatrix& worldMatrix)
 
    {
        if (fWorldMatrix != worldMatrix) {
            
            if (fRenderItem) {
                fRenderItem->setMatrix(&worldMatrix);
            }
            
            fWorldMatrix = worldMatrix;
            
            if (fType == MRenderItem::MaterialSceneItem) {
                MRenderer::setLightsAndShadowsDirty();
            }
            notifyWorldMatrixChange();
        }
    }
    void setBuffers(SubSceneOverride&                               subSceneOverride,
                    const boost::shared_ptr<const IndexBuffer>&     indices,
                    const boost::shared_ptr<const VertexBuffer>&    positions,
                    const boost::shared_ptr<const VertexBuffer>&    normals,
                    const boost::shared_ptr<const VertexBuffer>&    uvs,
    {
        const bool buffersChanged =
            fIndices    !=  indices     ||
            fPositions  !=  positions   ||
            fNormals    !=  normals     ||
            fUVs        !=  uvs;
        if (buffersChanged) {
            
            BuffersCache::getInstance().updateBuffers(
                subSceneOverride,
                fRenderItem,
                indices,
                positions,
                normals,
                uvs,
                boundingBox,
                fIndices,
                fPositions,
                fNormals,
                fUVs
            );
            
            fIndices        =   indices;
            fPositions      =   positions;
            fNormals        =   normals;
            fUVs            =   uvs;
            fBoundingBox    =   boundingBox;
            
            if (fType == MRenderItem::MaterialSceneItem) {
                MRenderer::setLightsAndShadowsDirty();
            }
            
            
            notifyInstancingClear();
        }
    }
    void setShader(const ShaderInstancePtr& shader)
    {
        if (fShader != shader) {
            
            if (fRenderItem) {
                fRenderItem->setShader(shader.get());
            }
            
            fShader = shader;
            
            
            notifyInstancingClear();
        }
    }
    void setCustomData(const boost::shared_ptr<MUserData>& userData)
    {
        if (fUserData != userData) {
            if (fRenderItem) {
                fRenderItem->setCustomData(userData.get());
            }
            fUserData = userData;
            notifyInstancingChange();
        }
    }
    void setDrawMode(MGeometry::DrawMode drawMode)
    {
        if (fDrawMode != drawMode) {
            if (fRenderItem) {
                fRenderItem->setDrawMode(drawMode);
            }
            fDrawMode = drawMode;
            notifyInstancingChange();
        }
    }
    void setSnappingSelectionMask()
    {
        if (!fIsPointSnapping) {
            if (fRenderItem) {
                fRenderItem->setSelectionMask(pointsForGravityMask);
            }
            fIsPointSnapping = true;
            notifyInstancingChange();
        }
    }
    void setDepthPriority(unsigned int depthPriority)
    {
        if (fDepthPriority != depthPriority) {
            if (fRenderItem) {
                fRenderItem->depthPriority(depthPriority);
            }
            fDepthPriority = depthPriority;
            notifyInstancingChange();
        }
    }
    void setExcludedFromPostEffects(bool exclude)
    {
        if (fExcludedFromPostEffects != exclude) {
            if (fRenderItem) {
                fRenderItem->setExcludedFromPostEffects(exclude);
            }
            fExcludedFromPostEffects = exclude;
            notifyInstancingChange();
        }
    }
    void setCastsShadows(bool cast)
    {
        if (fCastsShadows != cast) {
            
            if (fRenderItem) {
                fRenderItem->castsShadows(cast);
            }
            
            fCastsShadows = cast;
            
            if (fType == MRenderItem::MaterialSceneItem) {
                MRenderer::setLightsAndShadowsDirty();
            }
            notifyInstancingChange();
        }
    }
    void setReceivesShadows(bool receive)
    {
        if (fReceivesShadows != receive) {
            
            if (fRenderItem) {
                fRenderItem->receivesShadows(receive);
            }
            
            fReceivesShadows = receive;
            notifyInstancingChange();
        }
    }
    
    
    
    void installHardwareInstanceData(const boost::shared_ptr<HardwareInstanceData>& data)
    {
        fHardwareInstanceData = data;
        notifyInstancingChange();
    }
    
    
    void removeHardwareInstanceData(SubSceneOverride&   subSceneOverride,
    {
        if (fHardwareInstanceData) {
            if (fHardwareInstanceData->isInstanced()) {
                
                if (fRenderItem) {
                    unloadItem(container);
                }
                assert(!fRenderItem);
                loadItem(subSceneOverride, container);
            }
            
            fHardwareInstanceData.reset();
        }
    }
    
    bool hasHardwareInstanceData()
    {
        return fHardwareInstanceData.get() != NULL;
    }
    
    {
        
        if (!fRenderItem) return;
        
        
        removeFromContainer(container);
        fRenderItem = NULL;
    }
    
    void loadItem(SubSceneOverride& subSceneOverride,
    {
        
        if (fRenderItem) return;
        
        fRenderItem = MRenderItem::Create(
            fName,
            fType,
            fPrimitive
        );
        assert(fRenderItem);
        
        addToContainer(container);
        
        fRenderItem->setCustomData(fUserData.get());
        fRenderItem->enable(fEnabled);
        fRenderItem->setMatrix(&fWorldMatrix);
        fRenderItem->setDrawMode(fDrawMode);
        fRenderItem->depthPriority(fDepthPriority);
        fRenderItem->setExcludedFromPostEffects(fExcludedFromPostEffects);
        fRenderItem->castsShadows(fCastsShadows);
        fRenderItem->receivesShadows(fReceivesShadows);
        fRenderItem->setShader(fShader.get());
        if (fIsPointSnapping)
        {
            fRenderItem->setSelectionMask(pointsForGravityMask);
        }
        else
        {
            fRenderItem->setSelectionMask(gpuCacheMask);
        }
        
        BuffersCache::getInstance().updateBuffers(
            subSceneOverride,
            fRenderItem,
            fIndices,
            fPositions,
            fNormals,
            fUVs,
            fBoundingBox,
            fIndices,
            fPositions,
            fNormals,
            fUVs
        );
    }
    
    
    const MString&                      name()
 const      { 
return fName; }
 
    const MGeometry::Primitive          primitive() const { return fPrimitive; }
    const boost::shared_ptr<MUserData>& userData() const  { return fUserData; }
    const boost::shared_ptr<const IndexBuffer>&  indices() const    { return fIndices; }
    const boost::shared_ptr<const VertexBuffer>& positions() const  { return fPositions; }
    const boost::shared_ptr<const VertexBuffer>& normals() const    { return fNormals; }
    const boost::shared_ptr<const VertexBuffer>& uvs() const        { return fUVs; }
    const MBoundingBox& boundingBox()
 const { 
return fBoundingBox; }
 
    bool                enabled() const                 { return fEnabled; }
    const MMatrix&      worldMatrix()
 const             { 
return fWorldMatrix; }
 
    MGeometry::DrawMode drawMode() const                { return fDrawMode; }
    unsigned int        depthPriority() const           { return fDepthPriority; }
    bool                excludedFromPostEffects() const { return fExcludedFromPostEffects; }
    bool                castsShadows() const            { return fCastsShadows; }
    bool                receivesShadows() const         { return fReceivesShadows; }
    const ShaderInstancePtr& shader() const { return fShader; }
    
    MRenderItem* wrappedItem()
 const { 
return fRenderItem; }
 
private:
    
    
    
    void notifyInstancingChange()
    {
        if (fHardwareInstanceData) {
            fHardwareInstanceData->notifyInstancingChange();
        }
    }
    
    void notifyInstancingClear()
    {
        if (fHardwareInstanceData) {
            fHardwareInstanceData->notifyInstancingClear();
        }
    }
    
    void notifyWorldMatrixChange()
    {
        if (fHardwareInstanceData) {
            fHardwareInstanceData->notifyWorldMatrixChange();
        }
    }
    
    void notifyDestroy()
    {
        if (fHardwareInstanceData) {
            fHardwareInstanceData->notifyDestroy();
        }
    }
private:
    const MGeometry::Primitive              fPrimitive;
    boost::shared_ptr<MUserData>            fUserData;
    boost::shared_ptr<const IndexBuffer>    fIndices;
    boost::shared_ptr<const VertexBuffer>   fPositions;
    boost::shared_ptr<const VertexBuffer>   fNormals;
    boost::shared_ptr<const VertexBuffer>   fUVs;
    bool                                    fEnabled;
    MGeometry::DrawMode                     fDrawMode;
    unsigned int                            fDepthPriority;
    bool                                    fIsPointSnapping;
    bool                                    fExcludedFromPostEffects;
    bool                                    fCastsShadows;
    bool                                    fReceivesShadows;
    ShaderInstancePtr                       fShader;
    boost::shared_ptr<HardwareInstanceData> fHardwareInstanceData;
};
class HardwareInstanceManagerImpl : boost::noncopyable
{
public:
    HardwareInstanceManagerImpl(SubSceneOverride& subSceneOverride)
        : fSubSceneOverride(subSceneOverride)
    {}
    ~HardwareInstanceManagerImpl()
    {}
    
    
    
    
    {
        
        removePendingInstances(container);
        
        updateInstanceTransforms();
        
        boost::unordered_set<HardwareInstanceData*> dirtySourceItems;
        extractDirtySourceItems(container, dirtySourceItems);
        
        boost::unordered_set<HardwareInstanceData*> dirtyCandidates;
        processDirtySourceItems(container, dirtySourceItems, dirtyCandidates);
        
        processCandidates(container, dirtyCandidates);
        
        loadPendingItems(container);
    }
    
    
    {
        
        
        assert(fInstancingChangeItems.empty());
        assert(fWorldMatrixChangeItems.empty());
        assert(fItemsPendingLoad.empty());
        assert(fItemsPendingRemove.empty());
        
        boost::unordered_set<RenderItemWrapper*> renderItems;
        BOOST_FOREACH (const HardwareInstance& hwInstance, fInstances) {
            renderItems.insert(hwInstance.master->renderItem());
            BOOST_FOREACH (HardwareInstanceData* data, hwInstance.sources) {
                renderItems.insert(data->renderItem());
            }
        }
        
        fInstancingChangeItems.clear();
        fWorldMatrixChangeItems.clear();
        fItemsPendingLoad.clear();
        fItemsPendingRemove.clear();
        fInstances.clear();
        
        
        
        BOOST_FOREACH (RenderItemWrapper* renderItem, renderItems) {
            renderItem->removeHardwareInstanceData(fSubSceneOverride, container);
        }
    }
    
    
    void notifyInstancingChange(HardwareInstanceData* data)
    {
        assert(data && data->renderItem());
        fInstancingChangeItems.insert(data);
    }
    
    
    void notifyWorldMatrixChange(HardwareInstanceData* data)
    {
        assert(data && data->renderItem() && data->isInstanced());
        fWorldMatrixChangeItems.insert(data);
    }
    
    
    
    void notifyInstancingClear(HardwareInstanceData* data, bool destroy = false)
    {
        assert(data && data->renderItem());
        
        fInstancingChangeItems.insert(data);
        fWorldMatrixChangeItems.erase(data);
        
        
        
        if (data->isInstanced()) {
            fItemsPendingLoad.insert(data);
        }
        
        if (data->isMasterItem()) {
            HardwareInstanceSet::iterator it = fInstances.find(data);
            assert(it != fInstances.end() && it->master == data);
            
            
            BOOST_FOREACH (HardwareInstanceData* sourceData, it->sources) {
                
                fInstancingChangeItems.insert(sourceData);
                fWorldMatrixChangeItems.erase(sourceData);
                
                
                
                if (sourceData->isInstanced()) {
                    fItemsPendingLoad.insert(sourceData);
                }
                
                sourceData->clearInstanceData();
            }
            
            if (data->isInstanced()) {
                
                
                if (!destroy) {
                    
                    MStatus stat = fSubSceneOverride.removeAllInstances(
 
                        *data->renderItem()->wrappedItem()
                    );
                    MStatAssert(stat);
                }
                
                
                fItemsPendingRemove.insert(data);
            }
            fInstances.erase(it);
            data->clearInstanceData();
        }
        else {
            HardwareInstanceData* master = data->masterData();
            if (master) {
                
                HardwareInstanceSet::iterator it = fInstances.find(master);
                assert(it != fInstances.end());
                
                assert(it->sources.find(data) != it->sources.end());
                it->sources.erase(data);
                
                if (!it->candidate) {
                    assert(master->isInstanced() && data->isInstanced());
                    MStatus stat = fSubSceneOverride.removeInstance(
 
                        *master->renderItem()->wrappedItem(),
                        data->instanceId()
                    );
                    MStatAssert(stat);
                }
                
                data->clearInstanceData();
            }
        }
    }
    
    
    
    void notifyDestroy(HardwareInstanceData* data)
    {
        assert(data && data->renderItem());
        
        notifyInstancingClear(data, true  );
        
        
        
        fInstancingChangeItems.erase(data);
        fWorldMatrixChangeItems.erase(data);
        fItemsPendingLoad.erase(data);
        fItemsPendingRemove.erase(data);
    }
    {
        unsigned int hardwareInstanceID(hardwareInstanceIndex);
        BOOST_FOREACH (const HardwareInstance& hwInstance, fInstances) {
            if (hwInstance.master->renderItem()->name() == renderItem.
name())
 
            {
                bool found = false;
                if (hwInstance.master->instanceId() == hardwareInstanceID)
                {
                    renderItemName = hwInstance.master->renderItem()->name();
                    found = true;
                }
                else
                {
                    BOOST_FOREACH (HardwareInstanceData* sourceData, hwInstance.sources) {
                        if (sourceData->instanceId() == hardwareInstanceID)
                        {
                            renderItemName = sourceData->renderItem()->name();
                            found = true;
                            break;
                        }
                    }
                }
                if (found)
                {
                    renderItemName.
split(
':', renderItemParts);
                    if (renderItemParts.
length() > 1 && renderItemParts[0].isUnsigned())
 
                    {
                        return renderItemParts[0].asUnsigned();
                    }
                }
            }
        }
        return -1;
    }
    void dump() const
    {
        using namespace std;
        ostringstream tmp;
        size_t hwInstCounter = 0;
        BOOST_FOREACH (const HardwareInstance& hwInstance, fInstances) {
            tmp << "HW Instance #" << (hwInstCounter++) << endl;
            tmp << '\t' << "Master: " 
                << hwInstance.master->renderItem()->name().asChar()
                << endl;
            tmp << '\t' << "Candidate: "
                << (hwInstance.candidate ? "true" : "false")
                << endl;
            tmp << '\t' << "Sources: "
                << hwInstance.sources.size()
                << endl;
            size_t sourceCounter = 0;
            BOOST_FOREACH (HardwareInstanceData* sourceData, hwInstance.sources) {
                tmp << '\t' << '\t' << "Source #" << (sourceCounter++)
                    << " " << sourceData->renderItem()->name().asChar()
                    << " instance ID: " << sourceData->instanceId()
                    << endl;
            }
        }
        printf("%s\n", tmp.str().c_str());
    }
private:
    
    
    {
        BOOST_FOREACH (HardwareInstanceData* data, fItemsPendingRemove) {
            data->renderItem()->unloadItem(container);
        }
        fItemsPendingRemove.clear();
    }
    
    
    
    {
        BOOST_FOREACH (HardwareInstanceData* data, fItemsPendingLoad) {
            assert(data);
            data->renderItem()->loadItem(fSubSceneOverride, container);
        }
        fItemsPendingLoad.clear();
    }
    
    
    void updateInstanceTransforms()
    {
        BOOST_FOREACH (HardwareInstanceData* data, fWorldMatrixChangeItems) {
            assert(data && data->isInstanced());
            if (!data || !data->isInstanced()) continue;
            
            HardwareInstanceData* master = data->masterData();
            assert(master && master->isInstanced());
            
            RenderItemWrapper* masterItem = master->renderItem();
            assert(masterItem);
            RenderItemWrapper* thisItem = data->renderItem();
            assert(thisItem);
            
            MStatus stat = fSubSceneOverride.updateInstanceTransform(
 
                *masterItem->wrappedItem(),
                data->instanceId(),
                thisItem->worldMatrix()
            );
            MStatAssert(stat);
        }
        fWorldMatrixChangeItems.clear();
    }
    
    
    
    void extractDirtySourceItems(
        boost::unordered_set<HardwareInstanceData*>& dirtySourceItems)
    {
        BOOST_FOREACH (HardwareInstanceData* data, fInstancingChangeItems) {
            assert(data);
            
            if (!data->isMasterItem()) {
                assert(fInstances.find(data) == fInstances.end());
                dirtySourceItems.insert(data);
                continue;
            }
            
            HardwareInstanceSet::iterator it = fInstances.find(data);
            assert(it != fInstances.end());
            if (it == fInstances.end()) continue;
            assert(it->master == data);
            if (it->master != data) continue;
            
            
            BOOST_FOREACH (HardwareInstanceData* sourceData, it->sources) {
                assert(sourceData);
                dirtySourceItems.insert(sourceData);
            }
            
            HardwareInstance hwInstance = *it;
            fInstances.erase(it);
            if (fInstances.get<1>().find(hwInstance.master) == fInstances.get<1>().end()) {
                
                fInstances.insert(hwInstance);
            }
            else {
                
                
                BOOST_FOREACH (HardwareInstanceData* sourceData, hwInstance.sources) {
                    dirtySourceItems.insert(sourceData);
                    if (sourceData->isInstanced()) {
                        sourceData->renderItem()->unloadItem(container);
                        fItemsPendingLoad.insert(sourceData);
                    }
                    sourceData->clearInstanceData();
                }
                dirtySourceItems.insert(data);
                if (data->isInstanced()) {
                    data->renderItem()->unloadItem(container);
                    fItemsPendingLoad.insert(data);
                }
                data->clearInstanceData();
            }
        }
        fInstancingChangeItems.clear();
    }
    
    
    void processDirtySourceItems(
        const boost::unordered_set<HardwareInstanceData*>& dirtySourceItems,
        boost::unordered_set<HardwareInstanceData*>&       dirtyCandidates)
    {
        
        BOOST_FOREACH (HardwareInstanceData* data, dirtySourceItems) {
            assert(data && !data->isMasterItem());
            assert(fInstances.find(data) == fInstances.end());
            
            HardwareInstanceData* master = data->masterData();
            if (master) {
                
                HardwareInstanceSet::iterator it = fInstances.find(master);
                assert(it != fInstances.end());
                if (it != fInstances.end()) {
                    assert(it->sources.find(data) != it->sources.end());
                    it->sources.erase(data);
                }
            }
            
            bool skipThisItem = false;
            HardwareInstanceSet::nth_index<1>::type::iterator it = 
                fInstances.get<1>().find(data);
            if (data->isInstanced()) {
                
                if (it != fInstances.get<1>().end()) {
                    if (data->masterData() == it->master) {
                        
                        
                        
                        it->sources.insert(data);
                        skipThisItem = true;
                    }
                    else {
                        
                        
                        leaveInstance(data, container);
                    }
                }
                else {
                    
                    
                    leaveInstance(data, container);
                }
            }
            if (!skipThisItem) {
                assert(!data->isInstanced());
                if (it != fInstances.get<1>().end()) {
                    if (it->candidate) {
                        
                        
                        joinCandidate(it->master, data);
                        
                        dirtyCandidates.insert(it->master);
                    }
                    else {
                        
                        
                        joinInstance(it->master, data, container);
                    }
                }
                else {
                    
                    
                    newCandidate(data);
                }
            }
        }
    }
    
    
    void processCandidates(
        const boost::unordered_set<HardwareInstanceData*>& dirtyCandidates)
    {
        BOOST_FOREACH (HardwareInstanceData* data, dirtyCandidates) {
            assert(data);
            HardwareInstanceSet::iterator it = fInstances.find(data);
            assert(it != fInstances.end());
            if (it == fInstances.end()) continue;
            assert(it->candidate && data == it->master);
            if (!it->candidate || data != it->master) continue;
            
            
            if (it->sources.size() + 1 < Config::hardwareInstancingThreshold()) {
                continue;
            }
            
            HardwareInstance hwInstance = *it;
            fInstances.erase(it);
            
            assert(hwInstance.master && !hwInstance.master->isInstanced());
            hwInstance.master->clearInstanceData();
            newInstance(hwInstance.master, container);
            
            BOOST_FOREACH (HardwareInstanceData* data, hwInstance.sources) {
                assert(data && !data->isInstanced() && !data->isMasterItem());
                data->clearInstanceData();
                joinInstance(hwInstance.master, data, container);
            }
        }
    }
    void newCandidate(HardwareInstanceData* source)
    {
        
        assert(source);
        assert(!source->isInstanced() && !source->isMasterItem());
        if (source->isInstanced() || source->isMasterItem()) return;
        
        source->setupCandidate(source);
        
        HardwareInstance hwInstance;
        hwInstance.master    = source;
        hwInstance.candidate = true;
        fInstances.insert(hwInstance);
    }
    void joinCandidate(HardwareInstanceData* master,
                       HardwareInstanceData* source)
    {
        
        assert(master);
        assert(!master->isInstanced() && master->isMasterItem());
        if (master->isInstanced() || !master->isMasterItem()) return;
        
        HardwareInstanceSet::iterator it = fInstances.find(master);
        assert(it != fInstances.end());
        if (it == fInstances.end()) return;
        assert(it->master == master && it->candidate);
        if (it->master != master || !it->candidate) return;
        assert(it->sources.find(source) == it->sources.end());
        
        assert(source);
        assert(!source->isInstanced() && !source->isMasterItem());
        if (source->isInstanced() || source->isMasterItem()) return;
        
        source->setupCandidate(master);
        it->sources.insert(source);
    }
    void newInstance(HardwareInstanceData* source,
    {
        
        assert(source);
        assert(!source->isInstanced() && !source->isMasterItem());
        if (source->isInstanced() || source->isMasterItem()) return;
        
        RenderItemWrapper* sourceItem = source->renderItem();
        assert(sourceItem);
        
        sourceItem->loadItem(fSubSceneOverride, container);
        fItemsPendingLoad.erase(source);
        
        unsigned int instanceId = fSubSceneOverride.addInstanceTransform(
            *sourceItem->wrappedItem(),
            sourceItem->worldMatrix()
        );
        assert(instanceId > 0);
        if (instanceId == 0) return;    
        
        source->setupInstance(source, instanceId);
        
        HardwareInstance hwInstance;
        hwInstance.master    = source;
        hwInstance.candidate = false;
        fInstances.insert(hwInstance);
    }
    void joinInstance(HardwareInstanceData* master, 
                      HardwareInstanceData* source,
    {
        
        assert(master);
        assert(master->isInstanced() && master->isMasterItem());
        if (!master->isInstanced() || !master->isMasterItem()) return;
        
        HardwareInstanceSet::iterator it = fInstances.find(master);
        assert(it != fInstances.end());
        if (it == fInstances.end()) return;
        assert(it->master == master && !it->candidate);
        if (it->master != master || it->candidate) return;
        assert(it->sources.find(source) == it->sources.end());
        
        assert(source);
        assert(!source->isInstanced() && !source->isMasterItem());
        if (source->isInstanced() || source->isMasterItem()) return;
        
        RenderItemWrapper* masterItem = master->renderItem();
        assert(masterItem);
        RenderItemWrapper* sourceItem = source->renderItem();
        assert(sourceItem);
        
        
        unsigned int instanceId = fSubSceneOverride.addInstanceTransform(
            *masterItem->wrappedItem(),
            sourceItem->worldMatrix()
        );
        assert(instanceId > 0);
        if (instanceId == 0) return;    
        
        sourceItem->unloadItem(container);
        fItemsPendingLoad.erase(source);
        
        source->setupInstance(master, instanceId);
        it->sources.insert(source);
    }
    void leaveInstance(HardwareInstanceData* source,
    {
        
        assert(source);
        assert(source->isInstanced() && !source->isMasterItem());
        if (!source->isInstanced() || source->isMasterItem()) return;
        HardwareInstanceData* master = source->masterData();
        assert(master);
        
        RenderItemWrapper* masterItem = master->renderItem();
        assert(masterItem);
        RenderItemWrapper* sourceItem = source->renderItem();
        assert(sourceItem);
        
        MStatus stat = fSubSceneOverride.removeInstance(
 
            *masterItem->wrappedItem(),
            source->instanceId()
        );
        if (!stat) return;  
        
        sourceItem->loadItem(fSubSceneOverride, container);
        
        source->clearInstanceData();
    }
    
    
    
    struct VisHash : std::unary_function<HardwareInstanceData*, std::size_t>
    {
        std::size_t operator()(const HardwareInstanceData* const& data) const
        {
            std::size_t seed = 0;
            const RenderItemWrapper* w = data->renderItem();
            assert(w);
            boost::hash_combine(seed, w->type());
            boost::hash_combine(seed, w->primitive());
            boost::hash_combine(seed, w->userData().get());
            boost::hash_combine(seed, w->indices().get());
            boost::hash_combine(seed, w->positions().get());
            boost::hash_combine(seed, w->normals().get());
            boost::hash_combine(seed, w->uvs().get());
            boost::hash_combine(seed, w->enabled());
            boost::hash_combine(seed, w->drawMode());
            boost::hash_combine(seed, w->depthPriority());
            boost::hash_combine(seed, w->excludedFromPostEffects());
            boost::hash_combine(seed, w->castsShadows());
            boost::hash_combine(seed, w->receivesShadows());
            boost::hash_combine(seed, w->shader().get());
            return seed;
        }
    };
    
    
    
    struct VisEqualTo : std::binary_function<HardwareInstanceData*, HardwareInstanceData*, bool>
    {
        bool operator()(const HardwareInstanceData* const& xData,
            const HardwareInstanceData* const& yData) const
        {
            const RenderItemWrapper* x = xData->renderItem();
            const RenderItemWrapper* y = yData->renderItem();
            assert(x && y);
            return x->type()                   == y->type() &&
                x->primitive()                 == y->primitive() &&
                x->userData().get()            == y->userData().get() &&
                x->indices().get()             == y->indices().get() &&
                x->positions().get()           == y->positions().get() &&
                x->normals().get()             == y->normals().get() &&
                x->uvs().get()                 == y->uvs().get() &&
                x->enabled()                   == y->enabled() &&
                x->drawMode()                  == y->drawMode() &&
                x->depthPriority()             == y->depthPriority() &&
                x->excludedFromPostEffects()   == y->excludedFromPostEffects() &&
                x->castsShadows()              == y->castsShadows() &&
                x->receivesShadows()           == y->receivesShadows() &&
                x->shader().get()              == y->shader().get();
        }
    };
    
    
    
    
    
    
    struct HardwareInstance
    {
        
        HardwareInstanceData* master;
        
        
        mutable bool candidate;
        
        
        mutable boost::unordered_set<HardwareInstanceData*> sources;
    };
    typedef boost::multi_index_container<
        HardwareInstance,
        boost::multi_index::indexed_by<
            
            boost::multi_index::hashed_unique<
                BOOST_MULTI_INDEX_MEMBER(HardwareInstance,HardwareInstanceData*,master)
            >,
            
            boost::multi_index::hashed_non_unique<
                BOOST_MULTI_INDEX_MEMBER(HardwareInstance,HardwareInstanceData*,master),
                VisHash,
                VisEqualTo
            >
        >
    > HardwareInstanceSet;
    
    SubSceneOverride&   fSubSceneOverride;
    
    HardwareInstanceSet fInstances;
    
    
    boost::unordered_set<HardwareInstanceData*> fInstancingChangeItems;
    boost::unordered_set<HardwareInstanceData*> fWorldMatrixChangeItems;
    boost::unordered_set<HardwareInstanceData*> fItemsPendingLoad;
    boost::unordered_set<HardwareInstanceData*> fItemsPendingRemove;
};
void HardwareInstanceData::notifyInstancingChange()
{
    fManager->notifyInstancingChange(this);
}
void HardwareInstanceData::notifyInstancingClear()
{
    fManager->notifyInstancingClear(this);
}
void HardwareInstanceData::notifyWorldMatrixChange()
{
    
    if (isInstanced()) {
        fManager->notifyWorldMatrixChange(this);
    }
}
void HardwareInstanceData::notifyDestroy()
{
    fManager->notifyDestroy(this);
}
class ModelCallbacks : boost::noncopyable
{
public:
    static ModelCallbacks& getInstance()
    {
        
        static ModelCallbacks sSingleton;
        return sSingleton;
    }
    ModelCallbacks()
    {
        
        
        fAttrsAffectAppearance.insert("visibility");
        fAttrsAffectAppearance.insert("lodVisibility");
        fAttrsAffectAppearance.insert("intermediateObject");
        fAttrsAffectAppearance.insert("template");
        fAttrsAffectAppearance.insert("renderLayerInfo");
        fAttrsAffectAppearance.insert("renderLayerId");
        fAttrsAffectAppearance.insert("renderLayerRenderable");
        fAttrsAffectAppearance.insert("renderLayerColor");
        fAttrsAffectAppearance.insert("drawOverride");
        fAttrsAffectAppearance.insert("overrideDisplayType");
        fAttrsAffectAppearance.insert("overrideLevelOfDetail");
        fAttrsAffectAppearance.insert("overrideShading");
        fAttrsAffectAppearance.insert("overrideTexturing");
        fAttrsAffectAppearance.insert("overridePlayback");
        fAttrsAffectAppearance.insert("overrideEnabled");
        fAttrsAffectAppearance.insert("overrideVisibility");
        fAttrsAffectAppearance.insert("overrideColor");
        fAttrsAffectAppearance.insert("useObjectColor");
        fAttrsAffectAppearance.insert("objectColor");
        fAttrsAffectAppearance.insert("ghosting");
        fAttrsAffectAppearance.insert("castsShadows");
        fAttrsAffectAppearance.insert("receiveShadows");
        
            TimeChangeCallback, this);
            "renderLayerChange", RenderLayerChangeCallback, this);
            "renderLayerManagerChange", RenderLayerChangeCallback, this);
        
        selectionChanged();
    }
    ~ModelCallbacks()
    {
    }
    void registerSubSceneOverride(const ShapeNode* shapeNode, SubSceneOverride* subSceneOverride)
    {
        assert(shapeNode);
        if (!shapeNode) return;
        assert(subSceneOverride);
        if (!subSceneOverride) return;
        
        fShapeNodes.insert(std::make_pair(shapeNode, subSceneOverride));
    }
    void deregisterSubSceneOverride(const ShapeNode* shapeNode)
    {
        assert(shapeNode);
        if (!shapeNode) return;
        
        fShapeNodes.erase(shapeNode);
    }
    
    void selectionChanged()
    {
        
        
        ShapeNodeNameMap currentSelection;
        for (
unsigned int i = 0, size = list.
length(); i < size; i++) {
 
                
                        const ShapeNode* shapeNode = (
const ShapeNode*)dagNode.
userNode();
 
                        if (shapeNode) {
                            currentSelection.insert(std::make_pair(dagIt.
fullPathName(), shapeNode));
                        }
                    }
                }
            }
        }
        
        BOOST_FOREACH (const ShapeNodeNameMap::value_type& val, fLastSelection) {
            if (currentSelection.find(val.first) == currentSelection.end()) {
                ShapeNodeSubSceneMap::iterator it = fShapeNodes.find(val.second);
                if (it != fShapeNodes.end() && it->second) {
                    it->second->dirtyEverything();
                }
            }
        }
        
        BOOST_FOREACH (const ShapeNodeNameMap::value_type& val, currentSelection) {
            if (fLastSelection.find(val.first) == fLastSelection.end()) {
                ShapeNodeSubSceneMap::iterator it = fShapeNodes.find(val.second);
                if (it != fShapeNodes.end() && it->second) {
                    it->second->dirtyEverything();
                }
            }
        }
        fLastSelection.swap(currentSelection);
    }
    
    void timeChanged()
    {
        BOOST_FOREACH (ShapeNodeSubSceneMap::value_type& val, fShapeNodes) {
            val.second->dirtyVisibility();   
            val.second->dirtyWorldMatrix();  
            val.second->dirtyStreams();      
            val.second->dirtyMaterials();    
        }
    }
    
    void renderLayerChanged()
    {
        BOOST_FOREACH (ShapeNodeSubSceneMap::value_type& val, fShapeNodes) {
            val.second->dirtyEverything();   
        }
    }
    bool affectAppearance(
const MString& attr)
 const 
    {
        return (fAttrsAffectAppearance.find(attr) != fAttrsAffectAppearance.cend());
    }
private:
    static void MayaExitingCallback(void* clientData)
    {
        
        BuffersCache::getInstance().clear();
        UnitBoundingBox::clear();
    }
    static void SelectionChangedCallback(void* clientData)
    {
        assert(clientData);
        static_cast<ModelCallbacks*>(clientData)->selectionChanged();
    }
    static void TimeChangeCallback(
MTime& time, 
void* clientData)
 
    {
        assert(clientData);
        static_cast<ModelCallbacks*>(clientData)->timeChanged();
    }
    static void RenderLayerChangeCallback(void* clientData)
    {
        assert(clientData);
        static_cast<ModelCallbacks*>(clientData)->renderLayerChanged();
    }
private:
    MCallbackId fMayaExitingCallback;
    MCallbackId fSelectionChangedCallback;
    MCallbackId fTimeChangeCallback;
    MCallbackId fRenderLayerChangeCallback;
    MCallbackId fRenderLayerManagerChangeCallback;
    typedef boost::unordered_map<MString,const ShapeNode*,MStringHash> ShapeNodeNameMap;
    typedef boost::unordered_map<const ShapeNode*,SubSceneOverride*>   ShapeNodeSubSceneMap;
    ShapeNodeNameMap     fLastSelection;
    ShapeNodeSubSceneMap fShapeNodes;
    boost::unordered_set<MString, MStringHash> fAttrsAffectAppearance;
};
void InstanceChangedCallback(
MDagPath& child, 
MDagPath& parent, 
void* clientData)
 
{
    assert(clientData);
    static_cast<SubSceneOverride*>(clientData)->dirtyEverything();
    static_cast<SubSceneOverride*>(clientData)->resetDagPaths();
}
void WorldMatrixChangedCallback(
MObject& transformNode,
 
    MDagMessage::MatrixModifiedFlags& modified,
    void* clientData)
{
    assert(clientData);
    static_cast<SubSceneOverride*>(clientData)->dirtyWorldMatrix();
}
{
    
    
    assert(clientData);
    
    static_cast<SubSceneOverride*>(clientData)->clearNodeDirtyCallbacks();
    
    static_cast<SubSceneOverride*>(clientData)->dirtyEverything();
}
void NodeDirtyCallback(
MObject& node, 
MPlug& plug, 
void* clientData)
 
{
    
    
    
    assert(clientData);
    if (ModelCallbacks::getInstance().affectAppearance(attr.name())) {
        static_cast<SubSceneOverride*>(clientData)->dirtyEverything();
    }
}
}
namespace GPUCache {
class SubSceneOverride::HardwareInstanceManager : boost::noncopyable
{
public:
    HardwareInstanceManager(SubSceneOverride& subSceneOverride)
        : fImpl(new HardwareInstanceManagerImpl(subSceneOverride))
    {}
    ~HardwareInstanceManager()
    {}
    {
        fImpl->processInstances(container);
    }
    {
        fImpl->resetInstances(container);
    }
    void installHardwareInstanceData(RenderItemWrapper::Ptr& renderItem)
    {
        if (!renderItem->hasHardwareInstanceData()) {
            boost::shared_ptr<HardwareInstanceData> data(
                new HardwareInstanceData(fImpl.get(), renderItem.get())
            );
            renderItem->installHardwareInstanceData(data);
        }
    }
    {
        return fImpl->instancePathIndex(renderItem, hardwareInstanceIndex);
    }
private:
    boost::shared_ptr<HardwareInstanceManagerImpl> fImpl;
};
class SubSceneOverride::HierarchyStat : boost::noncopyable
{
public:
    typedef boost::shared_ptr<const HierarchyStat> Ptr;
    
    struct SubNodeStat
    {
        
        bool   isVisibilityAnimated;
        
        bool   isXformAnimated;
        
        bool   isShapeAnimated;
        
        bool   isDiffuseColorAnimated;
        
        size_t nextSubNodeIndex;
        
        size_t nextShapeSubNodeIndex;
        SubNodeStat()
            : isVisibilityAnimated(false),
              isXformAnimated(false),
              isShapeAnimated(false),
              isDiffuseColorAnimated(false),
              nextSubNodeIndex(0),
              nextShapeSubNodeIndex(0)
        {}
    };
    ~HierarchyStat() {}
    void setStat(size_t subNodeIndex, SubNodeStat& stat)
    {
        if (subNodeIndex >= fStats.size()) {
            fStats.resize(subNodeIndex+1);
        }
        fStats[subNodeIndex] = stat;
    }
    const SubNodeStat& stat(size_t subNodeIndex) const
    { return fStats[subNodeIndex]; }
private:
    HierarchyStat() {}
    friend class HierarchyStatVisitor;
    std::vector<SubNodeStat> fStats;
};
class SubSceneOverride::HierarchyStatVisitor : public SubNodeVisitor
{
public:
    HierarchyStatVisitor(const SubNode::Ptr& geometry)
        : fGeometry(geometry),
          fIsParentVisibilityAnimated(false),
          fIsVisibilityAnimated(false),
          fIsParentXformAnimated(false),
          fIsXformAnimated(false),
          fIsShapeAnimated(false),
          fIsDiffuseColorAnimated(false),
          fSubNodeIndex(0),
          fShapeSubNodeIndex(0)
    {
        fHierarchyStat.reset(new HierarchyStat());
    }
    virtual ~HierarchyStatVisitor()
    {}
    const HierarchyStat::Ptr getStat() const
    { return fHierarchyStat; }
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        
        size_t thisSubNodeIndex = fSubNodeIndex;
        fSubNodeIndex++;
        
        bool isVisibilityAnimated = false;
        if (xform.getSamples().size() > 1) {
            const boost::shared_ptr<const XformSample>& sample =
                xform.getSamples().begin()->second;
            if (sample) {
                const bool oneVisibility = sample->visibility();
                BOOST_FOREACH (const XformData::SampleMap::value_type& val, xform.getSamples()) {
                    if (val.second && val.second->visibility() != oneVisibility) {
                        isVisibilityAnimated = true;
                        break;
                    }
                }
            }
        }
        
        bool isXformAnimated = false;
        if (xform.getSamples().size() > 1) {
            const boost::shared_ptr<const XformSample>& sample =
                xform.getSamples().begin()->second;
            if (sample) {
                const MMatrix& oneMatrix = sample->xform();
 
                BOOST_FOREACH (const XformData::SampleMap::value_type& val, xform.getSamples()) {
                    if (val.second && val.second->xform() != oneMatrix) {
                        isXformAnimated = true;
                        break;
                    }
                }
            }
        }
        
        {
            ScopedGuard<bool> parentVisibilityGuard(fIsParentVisibilityAnimated);
            fIsParentVisibilityAnimated = fIsParentVisibilityAnimated || isVisibilityAnimated;
            ScopedGuard<bool> parentXformGuard(fIsParentXformAnimated);
            fIsParentXformAnimated = fIsParentXformAnimated || isXformAnimated;
            
            bool isShapeAnimated        = false;
            bool isDiffuseColorAnimated = false;
            
            BOOST_FOREACH (const SubNode::Ptr& child, subNode.getChildren()) {
                child->accept(*this);
                
                isVisibilityAnimated   = isVisibilityAnimated   || fIsVisibilityAnimated;
                isXformAnimated        = isXformAnimated        || fIsXformAnimated;
                isShapeAnimated        = isShapeAnimated        || fIsShapeAnimated;
                isDiffuseColorAnimated = isDiffuseColorAnimated || fIsDiffuseColorAnimated;
            }
            
            fIsVisibilityAnimated   = isVisibilityAnimated;
            fIsXformAnimated        = isXformAnimated;
            fIsShapeAnimated        = isShapeAnimated;
            fIsDiffuseColorAnimated = isDiffuseColorAnimated;
        }
        appendStat(thisSubNodeIndex);
    }
    virtual void visit(const ShapeData&   shape,
                       const SubNode&     subNode)
    {
        
        size_t thisSubNodeIndex = fSubNodeIndex;
        fSubNodeIndex++;
        fShapeSubNodeIndex++;
        
        fIsShapeAnimated = shape.getSamples().size() > 1;
        
        fIsDiffuseColorAnimated = false;
        if (fIsShapeAnimated) {
            const boost::shared_ptr<const ShapeSample>& sample =
                shape.getSamples().begin()->second;
            if (sample) {
                const MColor& oneColor = sample->diffuseColor();
 
                BOOST_FOREACH (const ShapeData::SampleMap::value_type& val, shape.getSamples()) {
                    if (val.second && val.second->diffuseColor() != oneColor) {
                        fIsDiffuseColorAnimated = true;
                        break;
                    }
                }
            }
        }
        
        fIsVisibilityAnimated = false;
        if (fIsShapeAnimated) {
            const boost::shared_ptr<const ShapeSample>& sample =
                shape.getSamples().begin()->second;
            if (sample) {
                const bool oneVisibility = sample->visibility();
                BOOST_FOREACH (const ShapeData::SampleMap::value_type& val, shape.getSamples()) {
                    if (val.second && val.second->visibility() != oneVisibility) {
                        fIsVisibilityAnimated = true;
                        break;
                    }
                }
            }
        }
        
        fIsXformAnimated = false;
        appendStat(thisSubNodeIndex);
    }
    void appendStat(size_t subNodeIndex)
    {
        
        HierarchyStat::SubNodeStat stat;
        stat.isVisibilityAnimated   = fIsVisibilityAnimated || fIsParentVisibilityAnimated;
        stat.isXformAnimated        = fIsXformAnimated || fIsParentXformAnimated;
        stat.isShapeAnimated        = fIsShapeAnimated;
        stat.isDiffuseColorAnimated = fIsDiffuseColorAnimated;
        stat.nextSubNodeIndex       = fSubNodeIndex;
        stat.nextShapeSubNodeIndex  = fShapeSubNodeIndex;
        fHierarchyStat->setStat(subNodeIndex, stat);
    }
private:
    const SubNode::Ptr fGeometry;
    bool               fIsParentVisibilityAnimated;
    bool               fIsVisibilityAnimated;
    bool               fIsParentXformAnimated;
    bool               fIsXformAnimated;
    bool               fIsShapeAnimated;
    bool               fIsDiffuseColorAnimated;
    size_t             fSubNodeIndex;
    size_t             fShapeSubNodeIndex;
    boost::shared_ptr<HierarchyStat> fHierarchyStat;
};
class SubSceneOverride::SubNodeRenderItems : boost::noncopyable
{
public:
    typedef boost::shared_ptr<SubNodeRenderItems> Ptr;
    SubNodeRenderItems()
        : fIsBoundingBoxPlaceHolder(false),
          fIsSelected(false),
          fVisibility(true),
          fValidPoly(true)
    {}
    ~SubNodeRenderItems()
    {}
    void updateRenderItems(SubSceneOverride&   subSceneOverride,
                           const ShapeData&    shape,
                           const SubNode&      subNode,
                           const bool          isSelected)
    {
        
        const boost::shared_ptr<const ShapeSample>& sample =
            shape.getSample(subSceneOverride.getTime());
        if (!sample) return;
        
        fIsBoundingBoxPlaceHolder = sample->isBoundingBoxPlaceHolder();
        fIsSelected               = isSelected;
        
        updateBoundingBoxItems(subSceneOverride, container, subNodePrefix, wireColor, subNode);
        
        updateSnappingItems(subSceneOverride, container, subNodePrefix);
        
        updateDormantWireItems(subSceneOverride, container, subNodePrefix, wireColor);
        
        updateActiveWireItems(subSceneOverride, container, subNodePrefix, wireColor);
        
        updateShadedItems(subSceneOverride, container, subNodePrefix, shape,
            sample->diffuseColor(), sample->numIndexGroups());
    }
    void updateVisibility(SubSceneOverride&   subSceneOverride,
                          const bool          visibility,
                          const ShapeData&    shape)
    {
        
        fVisibility = visibility;
        
        toggleBoundingBoxItem();
        toggleSnappingItem();
        toggleDormantWireItem();
        toggleActiveWireItem();
        toggleShadedItems();
    }
    void updateWorldMatrix(SubSceneOverride&   subSceneOverride,
                           const ShapeData&    shape)
    {
        
        if (fBoundingBoxItem) {
            const boost::shared_ptr<const ShapeSample>& sample =
                shape.getSample(subSceneOverride.getTime());
            if (sample) {
                    UnitBoundingBox::boundingBoxMatrix(boundingBox) * matrix;
                fBoundingBoxItem->setWorldMatrix(worldMatrix);
            }
        }
        if (fSnappingItem) {
            fSnappingItem->setWorldMatrix(matrix);
        }
        if (fDormantWireItem) {
            fDormantWireItem->setWorldMatrix(matrix);
        }
        if (fActiveWireItem) {
            fActiveWireItem->setWorldMatrix(matrix);
        }
        BOOST_FOREACH (RenderItemWrapper::Ptr& shadedItem, fShadedItems) {
            shadedItem->setWorldMatrix(matrix);
        }
    }
    void updateStreams(SubSceneOverride&   subSceneOverride,
                       const ShapeData&    shape)
    {
        const boost::shared_ptr<const ShapeSample>& sample =
            shape.getSample(subSceneOverride.getTime());
        if (!sample) return;
        
        fValidPoly = sample->numVerts() > 0 &&
                     sample->numWires() > 0 &&
                     sample->numTriangles() > 0 &&
                     sample->positions();
        
        toggleBoundingBoxItem();
        toggleSnappingItem();
        toggleDormantWireItem();
        toggleActiveWireItem();
        toggleShadedItems();
        if (!fValidPoly) {
            
            return;
        }
        if (fSnappingItem) {
            
            
            fSnappingItem->setBuffers(
                subSceneOverride,
                boost::shared_ptr<const IndexBuffer>(),
                sample->positions(),
                boost::shared_ptr<const VertexBuffer>(),
                boost::shared_ptr<const VertexBuffer>(),
                sample->boundingBox()
            );
        }
        
        if (fDormantWireItem) {
            fDormantWireItem->setBuffers(
                subSceneOverride,
                sample->wireVertIndices(),
                sample->positions(),
                boost::shared_ptr<const VertexBuffer>(),
                boost::shared_ptr<const VertexBuffer>(),
                sample->boundingBox()
            );
        }
        if (fActiveWireItem) {
            fActiveWireItem->setBuffers(
                subSceneOverride,
                sample->wireVertIndices(),
                sample->positions(),
                boost::shared_ptr<const VertexBuffer>(),
                boost::shared_ptr<const VertexBuffer>(),
                sample->boundingBox()
            );
        }
        
        for (size_t groupId = 0; groupId < sample->numIndexGroups(); groupId++) {
            if (groupId >= fShadedItems.size()) break;  
            assert(fShadedItems[groupId]);
            if (!fShadedItems[groupId]) continue;
            fShadedItems[groupId]->setBuffers(
                subSceneOverride,
                sample->triangleVertIndices(groupId),
                sample->positions(),
                sample->normals(),
                sample->uvs(),
                sample->boundingBox()
            );
        }
    }
    void updateMaterials(SubSceneOverride&   subSceneOverride,
                         const ShapeData&    shape)
    {
        const boost::shared_ptr<const ShapeSample>& sample =
            shape.getSample(subSceneOverride.getTime());
        if (!sample) return;
        for (size_t groupId = 0; groupId < sample->numIndexGroups(); groupId++) {
            if (groupId >= fShadedItems.size()) break;  
            if (groupId >= fSharedDiffuseColorShaders.size()) break;
            if (groupId >= fUniqueDiffuseColorShaders.size()) break;
            if (groupId >= fMaterialShaders.size()) break;
            assert(fShadedItems[groupId]);
            if (!fShadedItems[groupId]) continue;
            
            ShaderInstancePtr shader = fMaterialShaders[groupId];
            if (shader) {
                
                continue;
            }
            
            shader = fUniqueDiffuseColorShaders[groupId];
            if (shader) {
                
                
                setDiffuseColor(shader.get(), sample->diffuseColor());
                continue;
            }
            
            shader = ShaderInstanceCache::getInstance().getSharedDiffuseColorShader(
                sample->diffuseColor());
            
            
            
            assert(fSharedDiffuseColorShaders[groupId]);  
            if (shader != fSharedDiffuseColorShaders[groupId]) {
                shader = ShaderInstanceCache::getInstance().getUniqueDiffuseColorShader(
                    sample->diffuseColor());
                fSharedDiffuseColorShaders[groupId].reset();
                fUniqueDiffuseColorShaders[groupId] = shader;
                fShadedItems[groupId]->setShader(shader);
            }
        }
    }
    void updateBoundingBoxItems(SubSceneOverride&   subSceneOverride,
                                const SubNode&      subNode)
    {
        if (!fIsBoundingBoxPlaceHolder) {
            
            if (fBoundingBoxItem) {
                fBoundingBoxItem->removeFromContainer(container);
                fBoundingBoxItem.reset();
            }
            return;
        }
        
        if (!fBoundingBoxItem) {
            
            const MString boundingBoxItemName = subNodePrefix + 
":boundingBox";
 
            fBoundingBoxItem.reset(new RenderItemWrapper(
                boundingBoxItemName,
                MRenderItem::NonMaterialSceneItem,
                MGeometry::kLines
            ));
            fBoundingBoxItem->setDrawMode((MGeometry::DrawMode)(MGeometry::kWireframe | MGeometry::kShaded | MGeometry::kTextured));
            fBoundingBoxItem->setDepthPriority(MRenderItem::sDormantWireDepthPriority);
            
            ShaderInstancePtr boundingBoxShader = 
                ShaderInstanceCache::getInstance().getSharedBoundingBoxPlaceHolderShader(wireColor);
            if (boundingBoxShader) {
                fBoundingBoxItem->setShader(boundingBoxShader);
            }
            
            fBoundingBoxItem->addToContainer(container);
            
            fBoundingBoxItem->setBuffers(
                subSceneOverride,
                UnitBoundingBox::indices(),
                UnitBoundingBox::positions(),
                boost::shared_ptr<const VertexBuffer>(),
                boost::shared_ptr<const VertexBuffer>(),
                UnitBoundingBox::boundingBox()
            );
            
            fBoundingBoxItem->setCustomData(
                boost::shared_ptr<MUserData>(new SubNodeUserData(subNode))
            );
        }
        
        ShaderInstancePtr boundingBoxShader = 
            ShaderInstanceCache::getInstance().getSharedBoundingBoxPlaceHolderShader(wireColor);
        if (boundingBoxShader) {
            fBoundingBoxItem->setShader(boundingBoxShader);
        }
        toggleBoundingBoxItem();
    }
    void updateSnappingItems(SubSceneOverride&   subSceneOverride,
    {
        if (fIsBoundingBoxPlaceHolder) {
            
            if (fSnappingItem) fSnappingItem->setEnabled(false);
            return;
        }
        
        if (!fSnappingItem) {
            
            const MString snappingItemName = subNodePrefix + 
":snapping";
 
            fSnappingItem.reset(new RenderItemWrapper(
                snappingItemName,
                MRenderItem::DecorationItem,
                MGeometry::kPoints
            ));
            fSnappingItem->setDepthPriority(MRenderItem::sSelectionDepthPriority);
            fSnappingItem->setSnappingSelectionMask();
            
            fSnappingItem->addToContainer(container);
        }
        
        boost::shared_ptr<HardwareInstanceManager>& hwInstanceManager = 
            subSceneOverride.hardwareInstanceManager();
        if (hwInstanceManager) {
            hwInstanceManager->installHardwareInstanceData(fSnappingItem);
        }
        toggleSnappingItem();
        
        ShaderInstancePtr snappingShader = ShaderInstanceCache::getInstance().getSharedPointShader();
        if (snappingShader) {
            fSnappingItem->setShader(snappingShader);
        }
    }
    void updateDormantWireItems(SubSceneOverride&   subSceneOverride,
    {
        if (fIsBoundingBoxPlaceHolder) {
            
            if (fDormantWireItem) fDormantWireItem->setEnabled(false);
            return;
        }
        
        if (!fDormantWireItem) {
            
            const MString dormantWireItemName = subNodePrefix + 
":dormantWire";
 
            fDormantWireItem.reset(new RenderItemWrapper(
                dormantWireItemName,
                MRenderItem::DecorationItem,
                MGeometry::kLines
            ));
            fDormantWireItem->setDrawMode(MGeometry::kWireframe);
            fDormantWireItem->setDepthPriority(MRenderItem::sDormantWireDepthPriority);
            
            fDormantWireItem->addToContainer(container);
        }
        
        boost::shared_ptr<HardwareInstanceManager>& hwInstanceManager = 
            subSceneOverride.hardwareInstanceManager();
        if (hwInstanceManager) {
            hwInstanceManager->installHardwareInstanceData(fDormantWireItem);
        }
        toggleDormantWireItem();
        
        ShaderInstancePtr dormantWireShader =
            (DisplayPref::wireframeOnShadedMode() == DisplayPref::kWireframeOnShadedFull)
            ? ShaderInstanceCache::getInstance().getSharedWireShader(wireColor)
            : ShaderInstanceCache::getInstance().getSharedWireShaderWithCB(wireColor);
        if (dormantWireShader) {
            fDormantWireItem->setShader(dormantWireShader);
        }
    }
    void updateActiveWireItems(SubSceneOverride&   subSceneOverride,
    {
        if (fIsBoundingBoxPlaceHolder) {
            
            if (fActiveWireItem)  fActiveWireItem->setEnabled(false);
            return;
        }
        if (!fActiveWireItem) {
            
            const MString activeWireItemName = subNodePrefix + 
":activeWire";
 
            fActiveWireItem.reset(new RenderItemWrapper(
                activeWireItemName,
                MRenderItem::DecorationItem,
                MGeometry::kLines
            ));
            fActiveWireItem->setDrawMode((MGeometry::DrawMode)(MGeometry::kWireframe | MGeometry::kShaded | MGeometry::kTextured));
            fActiveWireItem->setDepthPriority(MRenderItem::sActiveWireDepthPriority);
            
            fActiveWireItem->addToContainer(container);
        }
        
        boost::shared_ptr<HardwareInstanceManager>& hwInstanceManager = 
            subSceneOverride.hardwareInstanceManager();
        if (hwInstanceManager) {
            hwInstanceManager->installHardwareInstanceData(fActiveWireItem);
        }
        toggleActiveWireItem();
        
        ShaderInstancePtr activeWireShader =
            (DisplayPref::wireframeOnShadedMode() == DisplayPref::kWireframeOnShadedFull)
            ? ShaderInstanceCache::getInstance().getSharedWireShader(wireColor)
            : ShaderInstanceCache::getInstance().getSharedWireShaderWithCB(wireColor);
        if (activeWireShader) {
            fActiveWireItem->setShader(activeWireShader);
        }
    }
    void updateShadedItems(SubSceneOverride&   subSceneOverride,
                           const ShapeData&    shape,
                           const size_t        nbIndexGroups)
    {
        
        if (fIsBoundingBoxPlaceHolder) {
            
            BOOST_FOREACH (RenderItemWrapper::Ptr& item, fShadedItems) {
                item->setEnabled(false);
            }
            return;
        }
        if (fShadedItems.empty()) {
            
            fShadedItems.reserve(nbIndexGroups);
            
            fSharedDiffuseColorShaders.reserve(nbIndexGroups);
            fUniqueDiffuseColorShaders.reserve(nbIndexGroups);
            fMaterialShaders.reserve(nbIndexGroups);
            for (size_t groupId = 0; groupId < nbIndexGroups; groupId++) {
                const MString shadedItemName = subNodePrefix + 
":shaded" + (int)groupId;
 
                RenderItemWrapper::Ptr renderItem(new RenderItemWrapper(
                    shadedItemName,
                    MRenderItem::MaterialSceneItem,
                    MGeometry::kTriangles
                ));
                renderItem->setDrawMode((MGeometry::DrawMode)(MGeometry::kShaded | MGeometry::kTextured));
                renderItem->setExcludedFromPostEffects(false);  
                fShadedItems.push_back(renderItem);
                
                ShaderInstancePtr shader;
                const std::vector<MString>&  materialsAssignment = shape.getMaterials();
                const MaterialGraphMap::Ptr& materials = subSceneOverride.getMaterial();
                if (materials && groupId < materialsAssignment.size()) {
                    const MaterialGraph::Ptr graph = materials->find(materialsAssignment[groupId]);
                    if (graph) {
                        shader = ShaderInstanceCache::getInstance().getSharedShadedMaterialShader(
                            graph, subSceneOverride.getTime()
                        );
                    }
                }
                if (shader) {
                    
                    renderItem->setShader(shader);
                    fMaterialShaders.push_back(shader);
                    fSharedDiffuseColorShaders.push_back(ShaderInstancePtr());
                    fUniqueDiffuseColorShaders.push_back(ShaderInstancePtr());
                }
                else {
                    
                    
                    
                    
                    ShaderInstancePtr sharedShader =
                        ShaderInstanceCache::getInstance().getSharedDiffuseColorShader(diffuseColor);
                    if (sharedShader) {
                        renderItem->setShader(sharedShader);
                    }
                    fMaterialShaders.push_back(ShaderInstancePtr());
                    fSharedDiffuseColorShaders.push_back(sharedShader);
                    fUniqueDiffuseColorShaders.push_back(ShaderInstancePtr());
                }
                
                renderItem->addToContainer(container);
            }
        }
        
        const bool castsShadows   = subSceneOverride.castsShadows();
        const bool receiveShadows = subSceneOverride.receiveShadows();
        BOOST_FOREACH (RenderItemWrapper::Ptr& renderItem, fShadedItems) {
            
            renderItem->setCastsShadows(castsShadows);
            renderItem->setReceivesShadows(receiveShadows);
            
            boost::shared_ptr<HardwareInstanceManager>& hwInstanceManager = 
                subSceneOverride.hardwareInstanceManager();
            if (hwInstanceManager && renderItem->shader() && !renderItem->shader()->isTransparent()) {
                hwInstanceManager->installHardwareInstanceData(renderItem);
            }
        }
        toggleShadedItems();
    }
    
    void toggleBoundingBoxItem()
    {
        if (fBoundingBoxItem) {
            if (fIsBoundingBoxPlaceHolder) {
                fBoundingBoxItem->setEnabled(fVisibility);
            }
            else {
                fBoundingBoxItem->setEnabled(false);
            }
        }
    }
    
    void toggleSnappingItem()
    {
        if (fSnappingItem) {
            if (fIsBoundingBoxPlaceHolder) {
                fSnappingItem->setEnabled(false);
            }
            else {
                fSnappingItem->setEnabled(fVisibility && fValidPoly);
            }
        }
    }
    
    void toggleDormantWireItem()
    {
        if (fDormantWireItem) {
            if (fIsBoundingBoxPlaceHolder) {
                fDormantWireItem->setEnabled(false);
            }
            else {
                fDormantWireItem->setEnabled(fVisibility && fValidPoly && !fIsSelected);
            }
        }
    }
    
    void toggleActiveWireItem()
    {
        if (fActiveWireItem) {
            if (fIsBoundingBoxPlaceHolder) {
                fActiveWireItem->setEnabled(false);
            }
            else {
                fActiveWireItem->setEnabled(fVisibility && fValidPoly && fIsSelected);
            }
        }
    }
    
    void toggleShadedItems()
    {
        BOOST_FOREACH (RenderItemWrapper::Ptr& shadedItem, fShadedItems) {
            if (fIsBoundingBoxPlaceHolder) {
                shadedItem->setEnabled(false);
            }
            else {
                shadedItem->setEnabled(fVisibility && fValidPoly);
            }
        }
    }
    void hideRenderItems()
    {
        
        if (fActiveWireItem) {
            fActiveWireItem->setEnabled(false);
        }
        if (fDormantWireItem) {
            fDormantWireItem->setEnabled(false);
        }
        if (fSnappingItem) {
            fSnappingItem->setEnabled(false);
        }
        if (fBoundingBoxItem) {
            fBoundingBoxItem->setEnabled(false);
        }
        BOOST_FOREACH (RenderItemWrapper::Ptr& item, fShadedItems) {
            item->setEnabled(false);
        }
    }
    {
        
        if (fActiveWireItem) {
            fActiveWireItem->removeFromContainer(container);
            fActiveWireItem.reset();
        }
        if (fDormantWireItem) {
            fDormantWireItem->removeFromContainer(container);
            fDormantWireItem.reset();
        }
        if (fSnappingItem) {
            fSnappingItem->removeFromContainer(container);
            fSnappingItem.reset();
        }
        if (fBoundingBoxItem) {
            fBoundingBoxItem->removeFromContainer(container);
            fBoundingBoxItem.reset();
        }
        BOOST_FOREACH (RenderItemWrapper::Ptr& item, fShadedItems) {
            item->removeFromContainer(container);
            item.reset();
        }
        fShadedItems.clear();
    }
private:
    
    RenderItemWrapper::Ptr               fBoundingBoxItem;
    RenderItemWrapper::Ptr               fActiveWireItem;
    RenderItemWrapper::Ptr               fDormantWireItem;
    RenderItemWrapper::Ptr               fSnappingItem;
    std::vector<RenderItemWrapper::Ptr>  fShadedItems;
    
    bool fIsBoundingBoxPlaceHolder; 
    bool fIsSelected;               
    bool fVisibility;               
    bool fValidPoly;                
    
    std::vector<ShaderInstancePtr>  fSharedDiffuseColorShaders;
    std::vector<ShaderInstancePtr>  fUniqueDiffuseColorShaders;
    std::vector<ShaderInstancePtr>  fMaterialShaders;
};
class SubSceneOverride::UpdateRenderItemsVisitor : public SubNodeVisitor
{
public:
    UpdateRenderItemsVisitor(SubSceneOverride&      subSceneOverride,
                             const bool             isSelected,
                             SubNodeRenderItemList& subNodeItems)
        : fSubSceneOverride(subSceneOverride),
          fContainer(container),
          fWireColor(wireColor),
          fIsSelected(isSelected),
          fSubNodeItems(subNodeItems),
          fLongName(instancePrefix),
          fSubNodeIndex(0)
    {}
    virtual ~UpdateRenderItemsVisitor()
    {}
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        
        ScopedGuard<MString> longNameGuard(fLongName);
        bool isTop = subNode.getParents().empty() && subNode.getName() == "|";
        if (!isTop) {
            fLongName += "|";
            fLongName += subNode.getName();
        }
        
        BOOST_FOREACH (const SubNode::Ptr& child, subNode.getChildren()) {
            child->accept(*this);
        }
    }
    virtual void visit(const ShapeData&   shape,
                       const SubNode&     subNode)
    {
        
        const MString prevName = fLongName;
 
        fLongName += "|";
        fLongName += subNode.getName();
        
        updateRenderItems(shape, subNode);
        fSubNodeIndex++;
        
        fLongName = prevName;
    }
    void updateRenderItems(const ShapeData& shape, const SubNode& subNode)
    {
        
        if (fSubNodeIndex >= fSubNodeItems.size()) {
            fSubNodeItems.push_back(boost::make_shared<SubNodeRenderItems>());
        }
        
        fSubNodeItems[fSubNodeIndex]->updateRenderItems(
            fSubSceneOverride,
            fContainer,
            fLongName,
            fWireColor,
            shape,
            subNode,
            fIsSelected
        );
    }
private:
    SubSceneOverride&      fSubSceneOverride;
    const bool             fIsSelected;
    SubNodeRenderItemList& fSubNodeItems;
    size_t  fSubNodeIndex;
};
template<typename DERIVED>
class SubSceneOverride::UpdateVisitorWithPrune : public SubNodeVisitor
{
public:
    UpdateVisitorWithPrune(SubSceneOverride&      subSceneOverride,
                           SubNodeRenderItemList& subNodeItems)
        : fSubSceneOverride(subSceneOverride),
          fContainer(container),
          fSubNodeItems(subNodeItems),
          fDontPrune(false),
          fTraverseInvisible(false),
          fSubNodeIndex(0),
          fShapeSubNodeIndex(0)
    {}
    virtual ~UpdateVisitorWithPrune()
    {}
    
    void setDontPrune(bool dontPrune)
    {
        fDontPrune = dontPrune;
    }
    
    void setTraverseInvisible(bool traverseInvisible)
    {
        fTraverseInvisible = traverseInvisible;
    }
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        
        const HierarchyStat::Ptr& hierarchyStat = fSubSceneOverride.getHierarchyStat();
        if (hierarchyStat) {
            const HierarchyStat::SubNodeStat& stat = hierarchyStat->stat(fSubNodeIndex);
            if (!fDontPrune) {
                if (static_cast<DERIVED*>(this)->canPrune(stat)) {
                    
                    
                    fSubNodeIndex      = stat.nextSubNodeIndex;
                    fShapeSubNodeIndex = stat.nextShapeSubNodeIndex;
                    return;
                }
                if (!fTraverseInvisible) {
                    const boost::shared_ptr<const XformSample>& sample =
                        xform.getSample(fSubSceneOverride.getTime());
                    if (sample && !sample->visibility()) {
                        
                        
                        fSubNodeIndex      = stat.nextSubNodeIndex;
                        fShapeSubNodeIndex = stat.nextShapeSubNodeIndex;
                        return;
                    }
                }
            }
        }
        fSubNodeIndex++;
        
        BOOST_FOREACH (const SubNode::Ptr& child, subNode.getChildren()) {
            child->accept(*this);
        }
    }
    virtual void visit(const ShapeData&   shape,
                       const SubNode&     subNode)
    {
        
        assert(fShapeSubNodeIndex < fSubNodeItems.size());
        if (fShapeSubNodeIndex < fSubNodeItems.size()) {
            static_cast<DERIVED*>(this)->update(shape, subNode, fSubNodeItems[fShapeSubNodeIndex]);
        }
        fSubNodeIndex++;
        fShapeSubNodeIndex++;
    }
protected:
    SubSceneOverride&      fSubSceneOverride;
    SubNodeRenderItemList& fSubNodeItems;
    bool                   fDontPrune;
    bool                   fTraverseInvisible;
    size_t  fSubNodeIndex;
    size_t  fShapeSubNodeIndex;
};
class SubSceneOverride::UpdateVisibilityVisitor :
    public SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateVisibilityVisitor>
{
public:
    typedef SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateVisibilityVisitor> ParentClass;
    UpdateVisibilityVisitor(SubSceneOverride&      subSceneOverride,
                            SubNodeRenderItemList& subNodeItems)
        : ParentClass(subSceneOverride, container, subNodeItems),
          fVisibility(true)
    {
        
        
        setTraverseInvisible(true);
    }
    virtual ~UpdateVisibilityVisitor()
    {}
    bool canPrune(const HierarchyStat::SubNodeStat& stat)
    {
        return !stat.isVisibilityAnimated;
    }
    void update(const ShapeData&         shape,
                const SubNode&           subNode,
                SubNodeRenderItems::Ptr& subNodeItems)
    {
        
        const boost::shared_ptr<const ShapeSample>& sample =
            shape.getSample(fSubSceneOverride.getTime());
        if (!sample) return;
        
        bool visibility = fVisibility && sample->visibility();
        subNodeItems->updateVisibility(
            fSubSceneOverride,
            fContainer,
            visibility,
            shape
        );
    }
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        
        const boost::shared_ptr<const XformSample>& sample =
            xform.getSample(fSubSceneOverride.getTime());
        if (!sample) return;
        
        ScopedGuard<bool> guard(fVisibility);
        fVisibility = fVisibility && sample->visibility();
        ParentClass::visit(xform, subNode);
    }
private:
    bool fVisibility;
};
class SubSceneOverride::UpdateWorldMatrixVisitor :
    public SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateWorldMatrixVisitor>
{
public:
    typedef SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateWorldMatrixVisitor> ParentClass;
    UpdateWorldMatrixVisitor(SubSceneOverride&      subSceneOverride,
                             SubNodeRenderItemList& subNodeItems)
        : ParentClass(subSceneOverride, container, subNodeItems),
          fMatrix(dagMatrix)
    {}
    virtual ~UpdateWorldMatrixVisitor()
    {}
    bool canPrune(const HierarchyStat::SubNodeStat& stat)
    {
        return !stat.isXformAnimated;
    }
    void update(const ShapeData&         shape,
                const SubNode&           subNode,
                SubNodeRenderItems::Ptr& subNodeItems)
    {
        subNodeItems->updateWorldMatrix(
            fSubSceneOverride,
            fContainer,
            fMatrix,
            shape
        );
    }
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        
        const boost::shared_ptr<const XformSample>& sample =
            xform.getSample(fSubSceneOverride.getTime());
        if (!sample) return;
        
        ScopedGuard<MMatrix> guard(fMatrix);
        fMatrix = sample->xform() * fMatrix;
        ParentClass::visit(xform, subNode);
    }
private:
};
class SubSceneOverride::UpdateStreamsVisitor :
    public SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateStreamsVisitor>
{
public:
    typedef SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateStreamsVisitor> ParentClass;
    UpdateStreamsVisitor(SubSceneOverride&      subSceneOverride,
                         SubNodeRenderItemList& subNodeItems)
        : ParentClass(subSceneOverride, container, subNodeItems)
    {}
    virtual ~UpdateStreamsVisitor()
    {}
    bool canPrune(const HierarchyStat::SubNodeStat& stat)
    {
        return !stat.isShapeAnimated;
    }
    void update(const ShapeData&         shape,
                const SubNode&           subNode,
                SubNodeRenderItems::Ptr& subNodeItems)
    {
        subNodeItems->updateStreams(
            fSubSceneOverride,
            fContainer,
            shape
        );
    }
};
class SubSceneOverride::UpdateDiffuseColorVisitor :
    public SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateDiffuseColorVisitor>
{
public:
    typedef SubSceneOverride::UpdateVisitorWithPrune<SubSceneOverride::UpdateDiffuseColorVisitor> ParentClass;
    UpdateDiffuseColorVisitor(SubSceneOverride&      subSceneOverride,
                              SubNodeRenderItemList& subNodeItems)
        : ParentClass(subSceneOverride, container, subNodeItems)
    {}
    virtual ~UpdateDiffuseColorVisitor()
    {}
    bool canPrune(const HierarchyStat::SubNodeStat& stat)
    {
        return !stat.isDiffuseColorAnimated;
    }
    void update(const ShapeData&         shape,
                const SubNode&           subNode,
                SubNodeRenderItems::Ptr& subNodeItems)
    {
        subNodeItems->updateMaterials(
            fSubSceneOverride,
            fContainer,
            shape
        );
    }
};
class SubSceneOverride::InstanceRenderItems : boost::noncopyable
{
public:
    typedef boost::shared_ptr<InstanceRenderItems> Ptr;
    InstanceRenderItems()
        : fVisibility(true),
          fVisibilityValid(false),
          fWorldMatrixValid(false),
          fStreamsValid(false),
          fMaterialsValid(false)
    {}
    ~InstanceRenderItems()
    {}
    
    void updateRenderItems(SubSceneOverride&   subSceneOverride,
    {
        
        fDagPath = dagPath;
        
        
        if (!fVisibility) {
            
            BOOST_FOREACH (SubNodeRenderItemList::value_type& items, fSubNodeItems) {
                items->hideRenderItems();
            }
            
            
            fVisibilityValid = false;
            return;
        }
        
            MGeometryUtilities::displayStatus(dagPath);
        fIsSelected = (displayStatus == 
kActive) ||
                      (displayStatus == kLead)   ||
        
        const MColor wireColor = MGeometryUtilities::wireframeColor(dagPath);
 
        
        if (!fBoundingBoxItem) {
            const MString boundingBoxName = instancePrefix + 
"BoundingBox";
 
            
            fBoundingBoxItem.reset(new RenderItemWrapper(
                boundingBoxName,
                MRenderItem::NonMaterialSceneItem,
                MGeometry::kLines
            ));
            fBoundingBoxItem->setDrawMode(MGeometry::kBoundingBox);
            
            fBoundingBoxShader =
                ShaderInstanceCache::getInstance().getSharedWireShader(wireColor);
            if (fBoundingBoxShader) {
                fBoundingBoxItem->setShader(fBoundingBoxShader);
            }
            
            fBoundingBoxItem->addToContainer(container);
            
            fBoundingBoxItem->setBuffers(
                subSceneOverride,
                UnitBoundingBox::indices(),
                UnitBoundingBox::positions(),
                boost::shared_ptr<const VertexBuffer>(),
                boost::shared_ptr<const VertexBuffer>(),
                UnitBoundingBox::boundingBox()
            );
        }
        
        fBoundingBoxShader =
            ShaderInstanceCache::getInstance().getSharedWireShader(wireColor);
        if (fBoundingBoxShader) {
            fBoundingBoxItem->setShader(fBoundingBoxShader);
        }
        
        fBoundingBoxItem->setDepthPriority(
            fIsSelected ?
                MRenderItem::sActiveWireDepthPriority :
                MRenderItem::sDormantWireDepthPriority
        );
        fBoundingBoxItem->setEnabled(true);
        
        UpdateRenderItemsVisitor visitor(subSceneOverride, container,
            instancePrefix, wireColor, fIsSelected, fSubNodeItems);
        subSceneOverride.getGeometry()->accept(visitor);
    }
    void updateVisibility(SubSceneOverride&   subSceneOverride,
    {
        assert(fDagPath.isValid());
        if (!fDagPath.isValid()) return;
        
        if (!fVisibility) {
            return;
        }
        
        UpdateVisibilityVisitor visitor(subSceneOverride, container, fSubNodeItems);
        visitor.setDontPrune(!fVisibilityValid);
        subSceneOverride.getGeometry()->accept(visitor);
        fVisibilityValid = true;
    }
    void updateWorldMatrix(SubSceneOverride&   subSceneOverride,
    {
        assert(fDagPath.isValid());
        if (!fDagPath.isValid()) return;
        
        if (!fVisibility) {
            return;
        }
        
        const MMatrix pathMatrix = fDagPath.inclusiveMatrix();
 
        const bool pathMatrixChanged = fMatrix != pathMatrix;
        fMatrix = pathMatrix;
        
        if (fBoundingBoxItem) {
            const MBoundingBox boundingBox = BoundingBoxVisitor::boundingBox(
 
                subSceneOverride.getGeometry(),
                subSceneOverride.getTime()
            );
                UnitBoundingBox::boundingBoxMatrix(boundingBox) * fMatrix;
            fBoundingBoxItem->setWorldMatrix(worldMatrix);
        }
        
        UpdateWorldMatrixVisitor visitor(subSceneOverride, container,
            fMatrix, fSubNodeItems);
        visitor.setDontPrune(pathMatrixChanged || !fWorldMatrixValid);  
        subSceneOverride.getGeometry()->accept(visitor);
        fWorldMatrixValid = true;
    }
    void updateStreams(SubSceneOverride&   subSceneOverride,
    {
        assert(fDagPath.isValid());
        if (!fDagPath.isValid()) return;
        
        if (!fVisibility) {
            return;
        }
        
        UpdateStreamsVisitor visitor(subSceneOverride, container, fSubNodeItems);
        visitor.setDontPrune(!fStreamsValid);
        subSceneOverride.getGeometry()->accept(visitor);
        fStreamsValid = true;
    }
    void updateMaterials(SubSceneOverride&   subSceneOverride,
    {
        assert(fDagPath.isValid());
        if (!fDagPath.isValid()) return;
        
        if (!fVisibility) {
            return;
        }
        
        UpdateDiffuseColorVisitor visitor(subSceneOverride, container, fSubNodeItems);
        visitor.setDontPrune(!fMaterialsValid);
        subSceneOverride.getGeometry()->accept(visitor);
        fMaterialsValid = true;
    }
    {
        
        if (fBoundingBoxItem) {
            fBoundingBoxItem->removeFromContainer(container);
            fBoundingBoxItem.reset();
        }
        
        BOOST_FOREACH (SubNodeRenderItems::Ptr& subNodeItem, fSubNodeItems) {
            subNodeItem->destroyRenderItems(container);
        }
    }
private:
    bool                   fIsSelected;
    bool                   fVisibility;
    RenderItemWrapper::Ptr fBoundingBoxItem;
    ShaderInstancePtr      fBoundingBoxShader;
    SubNodeRenderItemList  fSubNodeItems;
    bool fVisibilityValid;
    bool fWorldMatrixValid;
    bool fStreamsValid;
    bool fMaterialsValid;
};
{
    return new SubSceneOverride(object);
}
void SubSceneOverride::clear()
{
    
    BuffersCache::getInstance().clear();
}
MIndexBuffer* SubSceneOverride::lookup(
const boost::shared_ptr<const IndexBuffer>& indices)
 
{
    
    return BuffersCache::getInstance().lookup(indices);
}
MVertexBuffer* SubSceneOverride::lookup(
const boost::shared_ptr<const VertexBuffer>& vertices)
 
{
    
    return BuffersCache::getInstance().lookup(vertices);
}
SubSceneOverride::SubSceneOverride(
const MObject& 
object)
      fObject(object),
      fShapeNode(NULL),
      fUpdateRenderItemsRequired(true),
      fUpdateVisibilityRequired(true),
      fUpdateWorldMatrixRequired(true),
      fUpdateStreamsRequired(true),
      fUpdateMaterialsRequired(true),
      fOutOfViewFrustum(false),
      fOutOfViewFrustumUpdated(false),
      fWireOnShadedMode(DisplayPref::kWireframeOnShadedFull)
{
    
    fShapeNode = (
const ShapeNode*)dagNode.
userNode();
    assert(fShapeNode);
    
    resetDagPaths();
    
    fCastsShadowsPlug   = dagNode.
findPlug(
"castsShadows", 
false);
    fReceiveShadowsPlug = dagNode.
findPlug(
"receiveShadows", 
false);
    
        dagPath, InstanceChangedCallback, this);
        dagPath, InstanceChangedCallback, this);
        dagPath, WorldMatrixChangedCallback, this);
    registerNodeDirtyCallbacks();
    ModelCallbacks::getInstance().registerSubSceneOverride(fShapeNode, this);
    fUpdateTime = boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time();
}
SubSceneOverride::~SubSceneOverride()
{
    
    ModelCallbacks::getInstance().deregisterSubSceneOverride(fShapeNode);
    
    fInstanceRenderItems.clear();
    fHardwareInstanceManager.reset();
}
{
    
}
{
    assert(fShapeNode);
    if (!fShapeNode) return false;
    MRenderer* renderer = MRenderer::theRenderer();
 
    if (!renderer) return false;
    
    if (fInstanceDagPaths.length() == 0) {
        SubSceneOverride* nonConstThis = const_cast<SubSceneOverride*>(this);
    }
    
    const bool hwInstancing = useHardwareInstancing();
    if ((hwInstancing && !fHardwareInstanceManager) ||
            (!hwInstancing && fHardwareInstanceManager)) {
        return true;
    }
    
    SubNode::Ptr          geometry = fShapeNode->getCachedGeometry();
    MaterialGraphMap::Ptr material = fShapeNode->getCachedMaterial();
    
    if (geometry != fGeometry || material != fMaterial) {
        return true;
    }
    
    if (fWireOnShadedMode != DisplayPref::wireframeOnShadedMode()) {
        return true;
    }
    
    
    
    
    if (geometry && frameContext.
getLightingMode() == MFrameContext::kLightDefault) {
 
        
        const MMatrix viewProjInv = frameContext.
getMatrix(MFrameContext::kViewProjInverseMtx);
 
        
        geometry->accept(visitor);
        bool outOfViewFrustum = true;
        for (unsigned int i = 0; i < fInstanceDagPaths.length(); i++) {
            const MMatrix worldInv = fInstanceDagPaths[i].inclusiveMatrixInverse();
 
            
            Frustum frustum(viewProjInv * worldInv,
            if (frustum.test(visitor.boundingBox()) != Frustum::kOutside) {
                outOfViewFrustum = false;
                break;
            }
        }
        
        if (outOfViewFrustum) {
            
            
            
            
            if (fOutOfViewFrustum && fOutOfViewFrustumUpdated) {
                return false;
            }
        }
        SubSceneOverride* nonConstThis = const_cast<SubSceneOverride*>(this);
        nonConstThis->fOutOfViewFrustum        = outOfViewFrustum;
        nonConstThis->fOutOfViewFrustumUpdated = false;
    }
    
    CacheFileEntry::BackgroundReadingState readingState = fShapeNode->backgroundReadingState();
    if (readingState != fReadingState) {
        
        return true;
    }
    if (readingState != CacheFileEntry::kReadingDone) {
        
        boost::posix_time::ptime currentTime =
            boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time();
        boost::posix_time::time_duration interval = currentTime - fUpdateTime;
        if (interval.total_milliseconds() >= (int)(Config::backgroundReadingRefresh() / 2)) {
            return true;
        }
        return false;
    }
    return fUpdateRenderItemsRequired ||
            fUpdateVisibilityRequired ||
            fUpdateWorldMatrixRequired ||
            fUpdateStreamsRequired ||
            fUpdateMaterialsRequired;
}
{
    assert(fShapeNode);
    if (!fShapeNode) return;
    
    if (fNodeDirtyCallbacks.length() == 0) {
        registerNodeDirtyCallbacks();
    }
    
    const bool hwInstancing = useHardwareInstancing();
    if (hwInstancing && !fHardwareInstanceManager) {
        
        dirtyRenderItems();     
        fHardwareInstanceManager.reset(new HardwareInstanceManager(*this));
    }
    else if (!hwInstancing && fHardwareInstanceManager) {
        
        fHardwareInstanceManager->resetInstances(container);
        fHardwareInstanceManager.reset();
    }
    
    
    
    BuffersCache::getInstance().shrink();
    
    
    SubNode::Ptr          geometry = fShapeNode->getCachedGeometry();
    MaterialGraphMap::Ptr material = fShapeNode->getCachedMaterial();
    
    fUpdateTime = boost::date_time::microsec_clock<boost::posix_time::ptime>::local_time();
    
    if (geometry != fGeometry || material != fMaterial) {
        
        fGeometry = geometry;
        fMaterial = material;
        
        fInstanceRenderItems.clear();
        fHierarchyStat.reset();
        dirtyEverything();
    }
    
    CacheFileEntry::BackgroundReadingState readingState = fShapeNode->backgroundReadingState();
    if (readingState != fReadingState || readingState != CacheFileEntry::kReadingDone) {
        
        
        fReadingState = readingState;
        dirtyEverything();
    }
    
    if (fWireOnShadedMode != DisplayPref::wireframeOnShadedMode()) {
        fWireOnShadedMode = DisplayPref::wireframeOnShadedMode();
        dirtyRenderItems();
    }
    
    
    if (fUpdateRenderItemsRequired) {
        updateRenderItems(container, frameContext);
        fUpdateRenderItemsRequired = false;
    }
    
    if (fUpdateVisibilityRequired) {
        updateVisibility(container, frameContext);
        fUpdateVisibilityRequired = false;
    }
    
    if (fUpdateWorldMatrixRequired) {
        updateWorldMatrix(container, frameContext);
        fUpdateWorldMatrixRequired = false;
    }
    
    if (fUpdateStreamsRequired) {
        updateStreams(container, frameContext);
        fUpdateStreamsRequired = false;
    }
    
    if (fUpdateMaterialsRequired) {
        updateMaterials(container, frameContext);
        fUpdateMaterialsRequired = false;
    }
    
    if (!fHierarchyStat && fReadingState == CacheFileEntry::kReadingDone && fGeometry) {
        HierarchyStatVisitor visitor(fGeometry);
        fGeometry->accept(visitor);
        fHierarchyStat = visitor.getStat();
        
        MRenderer::setLightsAndShadowsDirty();
    }
    
    if (fHardwareInstanceManager) {
        fHardwareInstanceManager->processInstances(container);
    }
    
    if (fOutOfViewFrustum) {
        fOutOfViewFrustumUpdated = true;
    }
}
{
    unsigned int pathIndex = -1;
    
    
    int hardwareInstanceIndex = intersection.
instanceID();
 
    if (hardwareInstanceIndex >= 0)
    {
        pathIndex = fHardwareInstanceManager->instancePathIndex(renderItem, hardwareInstanceIndex);
    }
    else
    {
        
        renderItem.
name().
split(
':', renderItemParts);
        if (renderItemParts.
length() > 1 && renderItemParts[0].isUnsigned())
 
        {
            pathIndex = renderItemParts[0].asUnsigned();
        }
    }
    if (pathIndex < fInstanceDagPaths.length())
    {
            dagPath.
set(fInstanceDagPaths[pathIndex]);
            return true;
    }
    return false;
}
{
    
    if (pointSnappingActive())
    {
    }
}
void SubSceneOverride::dirtyEverything()
{
    dirtyRenderItems();
    dirtyVisibility();
    dirtyWorldMatrix();
    dirtyStreams();
    dirtyMaterials();
}
void SubSceneOverride::dirtyRenderItems()
{
    fUpdateRenderItemsRequired = true;
}
void SubSceneOverride::dirtyVisibility()
{
    fUpdateVisibilityRequired = true;
}
void SubSceneOverride::dirtyWorldMatrix()
{
    fUpdateWorldMatrixRequired = true;
}
void SubSceneOverride::dirtyStreams()
{
    fUpdateStreamsRequired = true;
}
void SubSceneOverride::dirtyMaterials()
{
    fUpdateMaterialsRequired = true;
}
void SubSceneOverride::resetDagPaths()
{
    fInstanceDagPaths.clear();
}
void SubSceneOverride::registerNodeDirtyCallbacks()
{
    assert(!fObject.isNull());
    if (fObject.isNull()) return;
    
    for (
unsigned int i = 0; i < paths.
length(); i++) {
 
        
            
                dagPath, ParentChangedCallback, this);
                dagPath, ParentChangedCallback, this);
            
                node, NodeDirtyCallback, this);
            
            fNodeDirtyCallbacks.append(parentAddedCallback);
            fNodeDirtyCallbacks.append(parentRemovedCallback);
            fNodeDirtyCallbacks.append(nodeDirtyCallback);
        }
    }
}
void SubSceneOverride::clearNodeDirtyCallbacks()
{
    if (fNodeDirtyCallbacks.length() > 0) {
        fNodeDirtyCallbacks.clear();
    }
}
{
    
    if (!fGeometry) {
        return;
    }
    
    unsigned int instanceCount = fInstanceDagPaths.length();
    if (instanceCount > fInstanceRenderItems.size()) {
        
        unsigned int difference = (unsigned int)(instanceCount - fInstanceRenderItems.size());
        for (unsigned int i = 0; i < difference; i++) {
            fInstanceRenderItems.push_back(
                boost::make_shared<InstanceRenderItems>());
        }
        
        MRenderer::setLightsAndShadowsDirty();
    }
    else if (instanceCount < fInstanceRenderItems.size()) {
        
        unsigned int difference = (unsigned int)(fInstanceRenderItems.size() - instanceCount);
        for (unsigned int i = 0; i < difference; i++) {
            fInstanceRenderItems.back()->destroyRenderItems(container);
            fInstanceRenderItems.pop_back();
        }
        
        MRenderer::setLightsAndShadowsDirty();
    }
    assert(fInstanceDagPaths.length() == fInstanceRenderItems.size());
    
    
    for (unsigned int i = 0; i < fInstanceRenderItems.size(); i++) {
        assert(fInstanceRenderItems[i]);
        
        
        
        fInstanceRenderItems[i]->updateRenderItems(
            *this, container, fInstanceDagPaths[i], instancePrefix);
    }
}
{
    
    if (!fGeometry) {
        return;
    }
    
    BOOST_FOREACH (InstanceRenderItems::Ptr& instance, fInstanceRenderItems) {
        instance->updateVisibility(*this, container);
    }
}
{
    
    if (!fGeometry) {
        return;
    }
    
    BOOST_FOREACH (InstanceRenderItems::Ptr& instance, fInstanceRenderItems) {
        instance->updateWorldMatrix(*this, container);
    }
}
{
    
    if (!fGeometry) {
        return;
    }
    
    BOOST_FOREACH (InstanceRenderItems::Ptr& instance, fInstanceRenderItems) {
        instance->updateStreams(*this, container);
    }
}
{
    
    if (!fGeometry) {
        return;
    }
    
    BOOST_FOREACH (InstanceRenderItems::Ptr& instance, fInstanceRenderItems) {
        instance->updateMaterials(*this, container);
    }
    
    ShaderInstanceCache::getInstance().updateCachedShadedShaders(fTimeInSeconds);
}
}