#ifndef _gpuCacheUtil_h_
#define _gpuCacheUtil_h_
#include <cassert>
#include <maya/MPlugArray.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnMesh.h>
#include <maya/MPlug.h>
#include <maya/MFloatArray.h>
#include <maya/MFloatVector.h>
#include <maya/MFloatVectorArray.h>
#include <maya/MFloatPoint.h>
#include <maya/MFloatPointArray.h>
#include <maya/MGlobal.h>
#include <maya/MStringResource.h>
#include <boost/shared_array.hpp>
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include <boost/foreach.hpp>
#include <map>
#include "gpuCacheSample.h"
#include "gpuCacheGeometry.h"
#include "gpuCacheMaterialNodes.h"
#ifdef _DEBUG
    #define MStatAssert(status) assert(status)
#else
    #define MStatAssert(status) ((void)status)
#endif
namespace GPUCache {
class ShapeVisibilityChecker
{
public:
    ShapeVisibilityChecker(
const MObject& shapeNode)
        : fShape(shapeNode)
    {}
    bool isVisible()
    {
        
        MPlug visibilityPlug = fShape.findPlug(
"visibility");
 
        assert(!visibilityPlug.
isNull());
        if (!visibilityPlug.
asBool()) {
 
            return false;
        }
        
        MPlug drawOverridePlug = fShape.findPlug(
"drawOverride");
 
        assert(!drawOverridePlug.
isNull());
        drawOverridePlug.
connectedTo(displayLayers, 
true, 
false);
        for (unsigned int i = 0; i < displayLayers.length(); i++) {
            MObject displayLayerNode = displayLayers[i].node();
 
                
                visibilityPlug = displayLayer.
findPlug(
"visibility");
                assert(!visibilityPlug.
isNull());
                if (!visibilityPlug.
asBool()) {
 
                    return false;
                }
            }
        }
        return true;
    }
private:
    
    ShapeVisibilityChecker(const ShapeVisibilityChecker&);
    const ShapeVisibilityChecker& operator=(const ShapeVisibilityChecker&);
};
template<class INDEX_TYPE>
class WireIndicesGenerator
{
public:
    typedef INDEX_TYPE index_type;
    WireIndicesGenerator(size_t numFaceCounts, const unsigned int* faceCounts,
                         size_t numFaceIndices, const index_type* faceIndices,
                         const index_type* mappedFaceIndices)
      : fNumFaceCounts(numFaceCounts), fFaceCounts(faceCounts),
        fNumFaceIndices(numFaceIndices), fFaceIndices(faceIndices),
        fMappedFaceIndices(mappedFaceIndices),
        fNumWires(0)
    {}
    void compute()
    {
        if (fNumFaceCounts == 0 || fNumFaceIndices == 0) {
            return;
        }
        
        size_t maxNumWires = fNumFaceIndices;
        boost::unordered_set<WirePair, typename WirePair::Hash, typename WirePair::EqualTo>
                wireSet(size_t(maxNumWires / 0.75f));
        
        size_t polyIndex = 0;
        size_t endOfPoly = fFaceCounts[polyIndex];
        for (size_t i = 0; i < fNumFaceIndices; i++) {
            
            
            
            
            
            index_type v1, v2, mappedV1, mappedV2;
            v1       = fFaceIndices[i];
            mappedV1 = fMappedFaceIndices[i];
            size_t v2Index;
            if (i + 1 == endOfPoly) {
                
                v2Index = i + 1 - fFaceCounts[polyIndex];
                if (++polyIndex < fNumFaceCounts) {
                    endOfPoly += fFaceCounts[polyIndex];
                }
            }
            else {
                v2Index = i + 1;
            }
            v2       = fFaceIndices[v2Index];
            mappedV2 = fMappedFaceIndices[v2Index];
            
            wireSet.insert(WirePair(v1, v2, mappedV1, mappedV2));
        }
        
        size_t numWires = wireSet.size();
        
        boost::shared_array<index_type> wireIndices(new index_type[numWires * 2]);
        size_t wireCount = 0;
        BOOST_FOREACH(const WirePair& pair, wireSet) {
            wireIndices[wireCount * 2 + 0] = pair.fMappedV1;
            wireIndices[wireCount * 2 + 1] = pair.fMappedV2;
            wireCount++;
        }
        fNumWires    = numWires;
        fWireIndices = wireIndices;
    }
    size_t                           numWires()    { return fNumWires; }
    boost::shared_array<index_type>& wireIndices() { return fWireIndices; }
private:
    
    WireIndicesGenerator(const WireIndicesGenerator&);
    const WireIndicesGenerator& operator= (const WireIndicesGenerator&);
    
    struct WirePair
    {
        WirePair(index_type v1, index_type v2, 
                 index_type mappedV1, index_type mappedV2)
            : fV1(v1), fV2(v2), fMappedV1(mappedV1), fMappedV2(mappedV2)
        {}
        struct Hash : std::unary_function<WirePair, std::size_t>
        {
            std::size_t operator()(const WirePair& pair) const
            {
                std::size_t seed = 0;
                if (pair.fV1 < pair.fV2) {
                    boost::hash_combine(seed, pair.fV1);
                    boost::hash_combine(seed, pair.fV2);
                }
                else {
                    boost::hash_combine(seed, pair.fV2);
                    boost::hash_combine(seed, pair.fV1);
                }
                return seed;
            }
        };
        struct EqualTo : std::binary_function<WirePair, WirePair, bool>
        {
            bool operator()(const WirePair& x, const WirePair& y) const
            {
                if (x.fV1 < x.fV2) {
                    if (y.fV1 < y.fV2) {
                        return (x.fV1 == y.fV1 && x.fV2 == y.fV2);
                    }
                    else {
                        return (x.fV1 == y.fV2 && x.fV2 == y.fV1);
                    }
                }
                else {
                    if (y.fV1 < y.fV2) {
                        return (x.fV2 == y.fV1 && x.fV1 == y.fV2);
                    }
                    else {
                        return (x.fV2 == y.fV2 && x.fV1 == y.fV1);
                    }
                }
            }
        };
        index_type fV1, fV2;
        index_type fMappedV1, fMappedV2;
    };
    
    size_t              fNumFaceCounts;
    const unsigned int* fFaceCounts;
    size_t              fNumFaceIndices;
    const index_type*   fFaceIndices;
    const index_type*   fMappedFaceIndices;
    
    size_t                          fNumWires;
    boost::shared_array<index_type> fWireIndices;
};
template<class INDEX_TYPE, size_t MAX_NUM_STREAMS = 16>
class MultiIndexedStreamsConverter
{
public:
    typedef INDEX_TYPE index_type;
    MultiIndexedStreamsConverter(size_t numFaceIndices, const index_type* faceIndices)
        : fNumFaceIndices(numFaceIndices), fFaceIndices(faceIndices), fNumStreams(0),
          fNumVertices(0)
    {
        
        addMultiIndexedStream(faceIndices);
    }
    void addMultiIndexedStream(const index_type* indices)
    {
        
        fStreams[fNumStreams++] = indices;
        assert(fNumStreams <= MAX_NUM_STREAMS);
    }
    void compute()
    {
        
        boost::shared_array<index_type> indicesRegion(
            new index_type[fNumStreams * fNumFaceIndices]);
        
        typedef boost::unordered_map<IndexTuple,size_t,typename IndexTuple::Hash,typename IndexTuple::EqualTo> IndicesMap;
        IndicesMap indicesMap(size_t(fNumFaceIndices / 0.75f));
        
        size_t vertexAttribIndex = 0;  
        boost::shared_array<index_type> mappedFaceIndices(new index_type[fNumFaceIndices]);
        for (size_t i = 0; i < fNumFaceIndices; i++) {
            
            index_type* indices = &indicesRegion[i * fNumStreams];
            
            for (unsigned int j = 0; j < fNumStreams; j++) {
                indices[j] = fStreams[j] ? fStreams[j][i] : (index_type)i;
            }
            
            IndexTuple tuple(indices, fNumStreams, (unsigned int)i);
            std::pair<typename IndicesMap::iterator,bool> ret = indicesMap.insert(std::make_pair(tuple, 0));
            if (ret.second) {
                
                ret.first->second = vertexAttribIndex++;
            }
            
            mappedFaceIndices[i] = (index_type)ret.first->second;
        }
        
        size_t numVertex = vertexAttribIndex;
        assert(vertexAttribIndex == indicesMap.size());
        
        boost::shared_array<unsigned int> vertAttribsIndices(new unsigned int[numVertex]);
        
        BOOST_FOREACH(const typename IndicesMap::value_type& pair, indicesMap) {
            vertAttribsIndices[pair.second] = pair.first.faceIndex();
        }
        fMappedFaceIndices  = mappedFaceIndices;
        fVertAttribsIndices = vertAttribsIndices;
        fNumVertices        = numVertex;
    }
    unsigned int                       numStreams()         { return fNumStreams; }
    size_t                             numVertices()        { return fNumVertices; }
    boost::shared_array<unsigned int>& vertAttribsIndices() { return fVertAttribsIndices; }
    boost::shared_array<index_type>&   mappedFaceIndices()  { return fMappedFaceIndices; }
private:
    
    MultiIndexedStreamsConverter(const MultiIndexedStreamsConverter&);
    const MultiIndexedStreamsConverter& operator= (const MultiIndexedStreamsConverter&);
    
    class IndexTuple
    {
    public:
        IndexTuple(index_type* indices, unsigned int size, unsigned int faceIndex)
            : fIndices(indices), fSize(size), fFaceIndex(faceIndex)
        {}
        const index_type& operator[](unsigned int index) const
        {
            assert(index < fSize);
            return fIndices[index];
        }
        unsigned int faceIndex() const 
        {
            return fFaceIndex;
        }
        struct Hash : std::unary_function<IndexTuple, std::size_t>
        {
            std::size_t operator()(const IndexTuple& tuple) const
            {
                std::size_t seed = 0;
                for (unsigned int i = 0; i < tuple.fSize; i++) {
                    boost::hash_combine(seed, tuple.fIndices[i]);
                }
                return seed;
            }
        };
        struct EqualTo : std::binary_function<IndexTuple, IndexTuple, bool>
        {
            bool operator()(const IndexTuple& x, const IndexTuple& y) const
            {
                if (x.fSize == y.fSize) {
                    return memcmp(x.fIndices, y.fIndices, sizeof(index_type) * x.fSize) == 0;
                }
                return false;
            }
        };
    private:
        index_type*  fIndices;
        unsigned int fFaceIndex;
        unsigned int fSize;
    };
    
    size_t            fNumFaceIndices;       
    const index_type* fFaceIndices;
    const index_type* fStreams[MAX_NUM_STREAMS];
    unsigned int      fNumStreams;
    
    size_t                            fNumVertices;
    boost::shared_array<unsigned int> fVertAttribsIndices;
    boost::shared_array<index_type>   fMappedFaceIndices;
};
template<class INDEX_TYPE, size_t SIZE>
class IndicesDropper
{
public:
    typedef INDEX_TYPE index_type;
    IndicesDropper(const float* attribArray, const index_type* indexArray, size_t numVerts)
    {
        
        boost::shared_array<float> mappedAttribs(new float[numVerts * SIZE]);
        for (size_t i = 0; i < numVerts; i++) {
            for (size_t j = 0; j < SIZE; j++) {
                mappedAttribs[i * SIZE + j] = attribArray[indexArray[i] * SIZE + j];
            }
        }
        fMappedAttribs = mappedAttribs;
    }
    boost::shared_array<float>& mappedAttribs() { return fMappedAttribs; }
private:
    
    IndicesDropper(const IndicesDropper&);
    const IndicesDropper& operator= (const IndicesDropper&);
    boost::shared_array<float> fMappedAttribs;
};
template<class INDEX_TYPE, size_t MAX_NUM_STREAMS = 16>
class MultiIndexedStreamsRemapper
{
public:
    typedef INDEX_TYPE index_type;
    MultiIndexedStreamsRemapper(const index_type* faceIndices,
            size_t numNewVertices, const unsigned int* vertAttribsIndices)
        : fFaceIndices(faceIndices), fNumNewVertices(numNewVertices),
          fVertAttribsIndices(vertAttribsIndices), fNumStreams(0)
    {}
    void addMultiIndexedStream(const float* attribs, const index_type* indices, bool faceVarying, int stride)
    {
        fAttribs[fNumStreams]     = attribs;
        fIndices[fNumStreams]     = indices;
        fFaceVarying[fNumStreams] = faceVarying;
        fStride[fNumStreams]      = stride;
        fNumStreams++;
    }
    void compute()
    {
        
        for (unsigned int i = 0; i < fNumStreams; i++) {
            const float*      attribs     = fAttribs[i];
            const index_type* indices     = fIndices[i];
            bool              faceVarying = fFaceVarying[i];
            int               stride      = fStride[i];
            
            boost::shared_array<float> mappedVertAttrib(
                new float[fNumNewVertices * stride]);
            for (size_t j = 0; j < fNumNewVertices; j++) {
                
                unsigned int polyVertIndex = fVertAttribsIndices[j];
                
                
                index_type pointOrPolyVertIndex = faceVarying ?
                            polyVertIndex : fFaceIndices[polyVertIndex];
                
                index_type attribIndex = indices ?
                            indices[pointOrPolyVertIndex] : pointOrPolyVertIndex;
                if (stride == 3) {
                    mappedVertAttrib[j * 3 + 0] = attribs[attribIndex * 3 + 0];
                    mappedVertAttrib[j * 3 + 1] = attribs[attribIndex * 3 + 1];
                    mappedVertAttrib[j * 3 + 2] = attribs[attribIndex * 3 + 2];
                }
                else if (stride == 2) {
                    mappedVertAttrib[j * 2 + 0] = attribs[attribIndex * 2 + 0];
                    mappedVertAttrib[j * 2 + 1] = attribs[attribIndex * 2 + 1];
                }
                else {
                    assert(0);
                }
            }
            fMappedVertAttribs[i] = mappedVertAttrib;
        }
    }
    boost::shared_array<float>& mappedVertAttribs(unsigned int index)
    {
        assert(index < fNumStreams);
        return fMappedVertAttribs[index];
    }
private:
    
    MultiIndexedStreamsRemapper(const MultiIndexedStreamsRemapper&);
    const MultiIndexedStreamsRemapper& operator= (const MultiIndexedStreamsRemapper&);
    
    const index_type*   fFaceIndices;
    size_t              fNumNewVertices;
    const unsigned int* fVertAttribsIndices;
    const float*      fAttribs[MAX_NUM_STREAMS];
    const index_type* fIndices[MAX_NUM_STREAMS];
    bool              fFaceVarying[MAX_NUM_STREAMS];
    int               fStride[MAX_NUM_STREAMS];
    unsigned int      fNumStreams;
    
    boost::shared_array<float> fMappedVertAttribs[MAX_NUM_STREAMS];
};
template<class INDEX_TYPE>
class PolyTriangulator
{
public:
    typedef INDEX_TYPE index_type;
    PolyTriangulator(size_t numFaceCounts, const unsigned int* faceCounts,
                     const index_type* faceIndices, bool faceIndicesCW,
                     const float* positions, const float* normals)
        : fNumFaceCounts(numFaceCounts), fFaceCounts(faceCounts),
          fFaceIndices(faceIndices), fFaceIndicesCW(faceIndicesCW),
          fPositions(positions), fNormals(normals),
          fNumTriangles(0)
    {}
    void compute()
    {
        
        if (fNumFaceCounts == 0) {
            return;
        }
        
        size_t maxPoints      = 0;  
        size_t totalTriangles = 0;  
        for (size_t i = 0; i < fNumFaceCounts; i++) {
            size_t numPoints = fFaceCounts[i];
            
            if (numPoints < 3) {
                continue;
            }
            
            maxPoints = std::max(numPoints, maxPoints);
            
            size_t numTriangles = numPoints - 2;
            totalTriangles += numTriangles;
        }
        size_t maxTriangles = maxPoints - 2;  
        
        boost::shared_array<index_type>     indices(new index_type[maxPoints]);
        boost::shared_array<unsigned short> triangles(new unsigned short[maxTriangles * 3]);
        boost::shared_array<float>          aPosition(new float[maxPoints * 2]);
        boost::shared_array<float>          aNormal;
        if (fNormals) {
            aNormal.reset(new float[maxPoints * 3]);
        }
        boost::shared_array<index_type> triangleIndices(new index_type[totalTriangles * 3]);
        
        size_t triangleCount  = 0;  
        for (size_t i = 0, polyVertOffset = 0; i < fNumFaceCounts; polyVertOffset += fFaceCounts[i], i++) {
            size_t numPoints = fFaceCounts[i];
            
            if (numPoints < 3) {
                continue;
            }
            
            if (numPoints == 3) {
                if (fFaceIndicesCW) {
                    triangleIndices[triangleCount * 3 + 0] = fFaceIndices[polyVertOffset + 2];
                    triangleIndices[triangleCount * 3 + 1] = fFaceIndices[polyVertOffset + 1];
                    triangleIndices[triangleCount * 3 + 2] = fFaceIndices[polyVertOffset + 0];
                }
                else {
                    triangleIndices[triangleCount * 3 + 0] = fFaceIndices[polyVertOffset + 0];
                    triangleIndices[triangleCount * 3 + 1] = fFaceIndices[polyVertOffset + 1];
                    triangleIndices[triangleCount * 3 + 2] = fFaceIndices[polyVertOffset + 2];
                }
                triangleCount++;
                continue;
            }
            
            if (fFaceIndicesCW)
            {
                for (size_t j = 0; j < numPoints; j++) {
                    size_t jCCW = numPoints - j - 1;
                    indices[j] = fFaceIndices[polyVertOffset + jCCW];
                }
            }
            else {
                for (size_t j = 0; j < numPoints; j++) {
                    indices[j] = fFaceIndices[polyVertOffset + j];
                }
            }
            
            for (size_t j = 0; j < numPoints; j++) {
                const float* thisPoint = &fPositions[indices[j] * 3];
                const float* nextPoint = &fPositions[indices[(j + numPoints - 1) % numPoints] * 3];
                faceNormal.
x += (thisPoint[1] - nextPoint[1]) * (thisPoint[2] + nextPoint[2]);
                faceNormal.
y += (thisPoint[2] - nextPoint[2]) * (thisPoint[0] + nextPoint[0]);
                faceNormal.
z += (thisPoint[0] - nextPoint[0]) * (thisPoint[1] + nextPoint[1]);
            }
            
            float cosa, sina, cosb, sinb, cacb, sacb;
            sinb = -sqrtf(faceNormal[0] * faceNormal[0] + faceNormal[1] * faceNormal[1]);
            if (sinb < -1e-5) {
                cosb =  faceNormal[2];
                sina =  faceNormal[1] / sinb;
                cosa = -faceNormal[0] / sinb;
                cacb = cosa * cosb;
                sacb = sina * cosb;
            }
            else {
                cacb = 1.0f;
                sacb = 0.0f;
                sinb = 0.0f;
                sina = 0.0f;
                if (faceNormal[2] > 0.0f) {
                    cosa = 1.0f;
                    cosb = 1.0f;
                }
                else {
                    cosa = -1.0f;
                    cosb = -1.0f;
                }
            }
            for (size_t j = 0; j < numPoints; j++) {
                const float* point = &fPositions[indices[j] * 3];
                aPosition[j * 2 + 0] = cacb * point[0] - sacb * point[1] + sinb * point[2];
                aPosition[j * 2 + 1] = sina * point[0] + cosa * point[1];
            }
            
            if (fNormals) {
                for (size_t j = 0; j < numPoints; j++) {
                    aNormal[j * 3 + 0] = fNormals[indices[j] * 3 + 0];
                    aNormal[j * 3 + 1] = fNormals[indices[j] * 3 + 1];
                    aNormal[j * 3 + 2] = fNormals[indices[j] * 3 + 2];
                }
            }
            
            int numResultTriangles = 0;
                aPosition.get(),
                (unsigned int)numPoints,
                (unsigned int)numPoints,
                0,
                fNormals != NULL,
                aNormal.get(),
                triangles.get(),
                numResultTriangles);
            if (numResultTriangles == int(numPoints - 2)) {
                
                for (size_t j = 0; j < size_t(numResultTriangles); j++) {
                    triangleIndices[triangleCount * 3 + 0] = indices[triangles[j * 3 + 0]];
                    triangleIndices[triangleCount * 3 + 1] = indices[triangles[j * 3 + 1]];
                    triangleIndices[triangleCount * 3 + 2] = indices[triangles[j * 3 + 2]];
                    triangleCount++;
                }
            }
            else {
                
                for (size_t j = 1; j < numPoints - 1; j++) {
                    triangleIndices[triangleCount * 3 + 0] = indices[0];
                    triangleIndices[triangleCount * 3 + 1] = indices[j];
                    triangleIndices[triangleCount * 3 + 2] = indices[j + 1];
                    triangleCount++;
                }
            }
        }
        fNumTriangles    = totalTriangles;
        fTriangleIndices = triangleIndices;
    }
    size_t numTriangles() { return fNumTriangles; }
    boost::shared_array<index_type>& triangleIndices() { return fTriangleIndices; }
private:
    
    PolyTriangulator(const PolyTriangulator&);
    const PolyTriangulator& operator= (const PolyTriangulator&);
    
    size_t              fNumFaceCounts;
    const unsigned int* fFaceCounts;
    const index_type*   fFaceIndices;
    bool                fFaceIndicesCW;
    const float*        fPositions;
    const float*        fNormals;
    
    size_t                          fNumTriangles;
    boost::shared_array<index_type> fTriangleIndices;
};
template<class INDEX_TYPE>
class MayaMeshExtractor
{
public:
    typedef INDEX_TYPE index_type;
    MayaMeshExtractor(
const MObject& meshObj)
        : fPolyMesh(meshObj), fWantUVs(true)
    {}
    void setWantUVs(bool wantUVs) { fWantUVs = wantUVs; }
    void compute()
    {
        bool needTriangulate = false;
        
        size_t                         numFaceCounts;
        boost::shared_array<unsigned int> faceCounts;
        
        size_t                       numFaceIndices;
        boost::shared_array<index_type> faceIndices;
        {
            status = fPolyMesh.getVertices(mayaVertexCount, mayaVertexList);
            
            numFaceCounts             = mayaVertexCount.
length();
            const int* srcVertexCount = &mayaVertexCount[0];
            faceCounts.reset(new unsigned int[numFaceCounts]);
            for (size_t i = 0; i < numFaceCounts; i++) {
                faceCounts[i] = srcVertexCount[i];
                if (faceCounts[i] != 3) needTriangulate = true;
            }
            numFaceIndices           = mayaVertexList.
length();
            const int* srcVertexList = &mayaVertexList[0];
            faceIndices.reset(new index_type[numFaceIndices]);
            for (size_t i = 0; i < numFaceIndices; i++) {
                faceIndices[i] = srcVertexList[i];
            }
        }
        
        boost::shared_array<float> positions;
        {
            status = fPolyMesh.getPoints(mayaPositions);
            
            unsigned int numPositions = mayaPositions.
length();
 
            positions.reset(new float[numPositions * 3]);
            
            for (unsigned int i = 0; i < numPositions; i++) {
                positions[i * 3 + 0] = point.
x;
                positions[i * 3 + 1] = point.
y;
                positions[i * 3 + 2] = point.
z;
            }
        }
        
        boost::shared_array<float>      normals;
        boost::shared_array<index_type> normalIndices;
        {
            status = fPolyMesh.getNormals(mayaNormals);
            status = fPolyMesh.getNormalIds(mayaNormalIdCounts, mayaNormalIds);
            
            unsigned int numNormals   = mayaNormals.
length();
 
            unsigned int numNormalIds = mayaNormalIds.
length();
 
            normals.reset(new float[numNormals * 3]);
            normalIndices.reset(new index_type[numNormalIds]);
            
            for (unsigned int i = 0; i < numNormals; i++) {
                normals[i * 3 + 0] = normal.
x;
                normals[i * 3 + 1] = normal.
y;
                normals[i * 3 + 2] = normal.
z;
            }
            for (unsigned int i = 0; i < numNormalIds; i++) {
                normalIndices[i] = mayaNormalIds[i];
            }
        }
        
        boost::shared_array<float>      UVs;
        boost::shared_array<index_type> uvIndices;
        if (fWantUVs) {
            status = fPolyMesh.getUVs(mayaUArray, mayaVArray);
            status = fPolyMesh.getAssignedUVs(mayaUVCounts, mayaUVIds);
            
            unsigned int numUVs   = mayaUArray.
length();
 
            unsigned int numUVIds = mayaUVIds.
length();
 
            if (numUVs > 0 && numUVIds > 0) {
                UVs.reset(new float[numUVs * 2]);
                uvIndices.reset(new index_type[numUVIds]);
                
                for (unsigned int i = 0; i < numUVs; i++) {
                    UVs[i * 2 + 0] = mayaUArray[i];
                    UVs[i * 2 + 1] = mayaVArray[i];
                }
                for (unsigned int i = 0; i < numUVIds; i++) {
                    uvIndices[i] = mayaUVIds[i];
                }
            }
        }
        
        size_t numVertices = 0;
        boost::shared_array<index_type>   mappedFaceIndices;
        boost::shared_array<unsigned int> vertAttribsIndices;
        {
            MultiIndexedStreamsConverter<index_type>
                converter(numFaceIndices, faceIndices.get());
            converter.addMultiIndexedStream(normalIndices.get());
            if (fWantUVs && uvIndices) {
                converter.addMultiIndexedStream(uvIndices.get());
            }
            converter.compute();
            numVertices        = converter.numVertices();
            mappedFaceIndices  = converter.mappedFaceIndices();
            vertAttribsIndices = converter.vertAttribsIndices();
        }
        
        boost::shared_array<float> mappedPositions;
        boost::shared_array<float> mappedNormals;
        boost::shared_array<float> mappedUVs;
        {
            MultiIndexedStreamsRemapper<index_type>
                remapper(faceIndices.get(), numVertices, vertAttribsIndices.get());
            remapper.addMultiIndexedStream(positions.get(), NULL, false, 3);
            remapper.addMultiIndexedStream(normals.get(), normalIndices.get(), true, 3);
            if (fWantUVs && UVs && uvIndices) {
                remapper.addMultiIndexedStream(UVs.get(), uvIndices.get(), true, 2);
            }
            remapper.compute();
            mappedPositions = remapper.mappedVertAttribs(0);
            mappedNormals   = remapper.mappedVertAttribs(1);
            if (fWantUVs && UVs && uvIndices) {
                mappedUVs = remapper.mappedVertAttribs(2);
            }
        }
        
        size_t numWires = 0;
        boost::shared_array<index_type> wireIndices;
        {
            WireIndicesGenerator<index_type> wireIndicesGenerator(
                numFaceCounts,  faceCounts.get(),
                numFaceIndices, faceIndices.get(),
                mappedFaceIndices.get());
            wireIndicesGenerator.compute();
            numWires    = wireIndicesGenerator.numWires();
            wireIndices = wireIndicesGenerator.wireIndices();
        }
        
        size_t numTriangles = 0;
        boost::shared_array<index_type> triangleIndices;
        if (needTriangulate) {
            PolyTriangulator<index_type> polyTriangulator(
                numFaceCounts, faceCounts.get(),
                mappedFaceIndices.get(), false,
                mappedPositions.get(), mappedNormals.get());
            polyTriangulator.compute();
            numTriangles    = polyTriangulator.numTriangles();
            triangleIndices = polyTriangulator.triangleIndices();
        }
        else {
            assert(numFaceIndices % 3 == 0);
            numTriangles    = numFaceIndices / 3;
            triangleIndices = mappedFaceIndices;
        }
        
        fWireIndices     = SharedArray<index_type>::create(
                wireIndices, numWires * 2);
        fTriangleIndices = SharedArray<index_type>::create(
                triangleIndices, numTriangles * 3);
        fPositions = SharedArray<float>::create(
                mappedPositions, numVertices * 3);
        fNormals   = SharedArray<float>::create(
                mappedNormals, numVertices * 3);
        if (fWantUVs && mappedUVs) {
            fUVs = SharedArray<float>::create(
                    mappedUVs, numVertices * 2);
        }
    }
    boost::shared_ptr<ReadableArray<index_type> >& triangleIndices()
    { return fTriangleIndices; }
    boost::shared_ptr<ReadableArray<index_type> >& wireIndices()
    { return fWireIndices; }
    boost::shared_ptr<ReadableArray<float> >& positions()
    { return fPositions; }
    boost::shared_ptr<ReadableArray<float> >& normals()
    { return fNormals; }
    boost::shared_ptr<ReadableArray<float> >& uvs()
    { return fUVs; }
private:
    
    MayaMeshExtractor(const MayaMeshExtractor&);
    const MayaMeshExtractor& operator= (const MayaMeshExtractor&);
    
    bool    fWantUVs;
    
    boost::shared_ptr<ReadableArray<index_type> > fTriangleIndices;
    boost::shared_ptr<ReadableArray<index_type> > fWireIndices;
    boost::shared_ptr<ReadableArray<float> >      fPositions;
    boost::shared_ptr<ReadableArray<float> >      fNormals;
    boost::shared_ptr<ReadableArray<float> >      fUVs;
};
class SubNodeTransparentTypeVisitor : public SubNodeVisitor
{
public:
    SubNodeTransparentTypeVisitor() {}
    virtual ~SubNodeTransparentTypeVisitor() {}
    virtual void visit(const XformData& xform,
                       const SubNode&   subNode)
    {
        
        fTransparentTypes.push_back(SubNode::kUnknown);
        
        BOOST_FOREACH (const SubNode::Ptr& child, subNode.getChildren()) {
            child->accept(*this);
        }
        
        const_cast<SubNode&>(subNode).setTransparentType(fTransparentTypes.back());
        fTransparentTypes.pop_back();
    }
    virtual void visit(const ShapeData& shape,
                       const SubNode&   subNode)
    {
        
        for (size_t i = 0; i < fTransparentTypes.size(); i++) {
            if (fTransparentTypes[i] == SubNode::kUnknown) {
                
                fTransparentTypes[i] = subNode.transparentType();
            }
            else {
                
                if (fTransparentTypes[i] != subNode.transparentType()) {
                    fTransparentTypes[i] = SubNode::kOpaqueAndTransparent;
                }
            }
        }
    }
private:
    std::vector<SubNode::TransparentType> fTransparentTypes;
};
class BoundingBoxVisitor :  public SubNodeVisitor
{
public:
    BoundingBoxVisitor(double timeInSeconds)
      : fTimeInSeconds(timeInSeconds)
    {}
    virtual ~BoundingBoxVisitor() {}
    
    { return fBoundingBox; }
    
    virtual void visit(const XformData&   xform,
                       const SubNode&     subNode)
    {
        const boost::shared_ptr<const XformSample>& sample =
            xform.getSample(fTimeInSeconds);
        if (sample) {
            fBoundingBox = sample->boundingBox();
        }
    }
    
    virtual void visit(const ShapeData&   shape,
                       const SubNode&     subNode)
    {
        const boost::shared_ptr<const ShapeSample>& sample =
            shape.getSample(fTimeInSeconds);
        if (sample) {
            fBoundingBox = sample->boundingBox();
        }
    }
    
    static MBoundingBox boundingBox(
const SubNode::Ptr& subNode,
 
                                    const double        timeInSeconds)
    {
        if (subNode) {
            BoundingBoxVisitor visitor(timeInSeconds);
            subNode->accept(visitor);
            return visitor.boundingBox();
        }
    }
private:
    const double fTimeInSeconds;
};
class ShapePathVisitor : public SubNodeVisitor
{
public:
    typedef std::pair<MString,const SubNode*> ShapePathAndSubNode;
    typedef std::vector<ShapePathAndSubNode>  ShapePathAndSubNodeList;
    ShapePathVisitor(ShapePathAndSubNodeList& shapePaths)
        : fShapePaths(shapePaths)
    {}
    virtual ~ShapePathVisitor() 
    {}
    virtual void visit(const XformData& xform,
                       const SubNode&   subNode)
    {
        
        bool isTop = subNode.getName() == "|";
        if (!isTop) fCurrentPath.push_back(subNode.getName());
        
        BOOST_FOREACH (const SubNode::Ptr& child, subNode.getChildren()) {
            child->accept(*this);
        }
        if (!isTop) fCurrentPath.pop_back();
    }
    virtual void visit(const ShapeData& shape,
                       const SubNode&   subNode)
    {
        
        for (size_t i = 0; i < fCurrentPath.size(); i++) {
            path += "|";
            path += fCurrentPath[i];
        }
        path += "|";
        path += subNode.getName();
        fShapePaths.push_back(
            std::make_pair(path, &subNode));
    }
private:
    ShapePathAndSubNodeList& fShapePaths;
    std::vector<MString>     fCurrentPath;
};
inline bool ReplaceSubNodeData(
const SubNode::Ptr& top, 
const SubNode::Ptr& node, 
const MString& path)
 
{
    
    
    if (steps.
length() == 0) 
return false;
 
    
    SubNode::Ptr firstNode;
    if (top->getName() == "|") {
        
        BOOST_FOREACH (const SubNode::Ptr& child, top->getChildren()) {
            if (child->getName() == steps[0]) {
                firstNode = child;
                break;
            }
        }
    }
    else {
        if (top->getName() == steps[0]) {
            firstNode = top;
        }
    }
    
    if (!firstNode) return false;
    
    SubNode::Ptr current = firstNode;
    for (
unsigned int i = 1; i < steps.
length(); i++) {
 
        bool found = false;
        BOOST_FOREACH (const SubNode::Ptr& child, current->getChildren()) {
            if (child->getName() == steps[i]) {
                current = child;
                found = true;
                break;
            }
        }
        
        if (!found) return false;
    }
    
    assert(current);
    assert(node);
    
    
    
    SubNode::MPtr mCurrent = boost::const_pointer_cast<SubNode>(current);
    SubNode::MPtr mNode    = boost::const_pointer_cast<SubNode>(node);
    SubNode::swapNodeData(mCurrent, mNode);
    return true;
}
inline bool ValidateGeomPath(
const SubNode::Ptr& top, 
const MString& geomPath, 
MString& validatedGeomPath)
 
{
    if( !top )
    {
        return false;
    }
    
    geomPath.
split(
'|', pathArray);
    bool valid = true;
        
    
    SubNode::Ptr current = top;
    for( 
unsigned int i = 0; i < pathArray.
length(); i++ )
 
    {
        bool foundChild = false;
        const std::vector<SubNode::Ptr>& children = current->getChildren();
        for( unsigned int j = 0; j < children.size(); j++ )
        {
            if( children[j]->getName() == step )
            {
                current = children[j];
                foundChild = true;
            }
        }
        if( !foundChild )
        {
            valid = false;
            break;
        }
        validatedGeomPath += step;
    }
    if (validatedGeomPath.
length() == 0) {
 
    }
    
    return valid;
}
inline bool CreateSubNodeHierarchy(
const SubNode::Ptr& top, 
const MString& geomPath, 
MString& validatedGeomPath, SubNode::Ptr& out)
 
{
    if( !top )
    {
        return false;
    }
    
    ValidateGeomPath( top, geomPath, validatedGeomPath );
    
    validatedGeomPath.
split(
'|', pathArray);
    {
        
        out = top;
    }
    else
    {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        SubNode::MPtr copyTop = SubNode::create( top->getName(), top->getData() );
        copyTop->setTransparentType( top->transparentType() );
        SubNode::MPtr copyCurrent = copyTop;
        
        SubNode::Ptr current = top;
        for( 
unsigned int i = 0; i < pathArray.
length(); i++ )
 
        {
            const std::vector<SubNode::Ptr>& children = current->getChildren();
            bool foundChild = false;
            for( unsigned int j = 0; j < children.size(); j++ )
            {
                if( children[j]->getName() == step )
                {
                    current = children[j];
                    foundChild = true;
                    break;
                }
            }
            assert(foundChild);
            SubNode::MPtr copyChild;
            if( i+1 < pathArray.
length() )
 
            {
                copyChild = SubNode::create( current->getName(), current->getData() );
                copyChild->setTransparentType( current->transparentType() );
            }
            else
            {
                
                copyChild = boost::const_pointer_cast<SubNode>(current);
            }
            SubNode::connect(copyCurrent, copyChild);
            copyCurrent = copyChild;
        }
        out = copyTop;
    }
    return true;
}
class InstanceMaterialLookup : boost::noncopyable
{
public:
    InstanceMaterialLookup(
const MDagPath& dagPath);
    ~InstanceMaterialLookup();
    
    bool    hasWholeObjectMaterial();
    MObject findWholeObjectShadingGroup();
 
    MObject findWholeObjectSurfaceMaterial();
 
    
    bool hasComponentMaterials();
    bool findShadingGroups(std::vector<MObject>& shadingGroups);
    bool findSurfaceMaterials(std::vector<MObject>& surfaceMaterials);
private:
    
    static MObject findShadingGroupByPlug(
const MPlug& srcPlug);
 
    static MObject findSurfaceMaterialByShadingGroup(
const MObject& shadingGroup);
 
    static void findObjectGroupsPlug(
const MPlug& iogPlug, std::vector<MPlug>& ogPlugs);
 
    const MPlug fInstObjGroupsPlug;
 
};
class ShadedModeColor : boost::noncopyable
{
public:
    static bool evaluateBool(const MaterialProperty::Ptr& prop,
                             double                       timeInSeconds);
    static float evaluateFloat(const MaterialProperty::Ptr& prop,
                               double                       timeInSeconds);
    static MColor evaluateDefaultColor(
const MaterialProperty::Ptr& prop,
 
                                       double                       timeInSeconds);
    static MColor evaluateColor(
const MaterialProperty::Ptr& prop,
 
                                double                       timeInSeconds);
};
{
    const wchar_t* buffer = msg.
asWChar();
 
    std::wstringstream stream;
    for (unsigned int i = 0; i < length; i++) {
        wchar_t ch = buffer[i];
        switch (ch) {
        case '\n': stream << L"\\n"; continue;
        case '\t': stream << L"\\t"; continue;
        case '\b': stream << L"\\b"; continue;
        case '\r': stream << L"\\r"; continue;
        case '\f': stream << L"\\f"; continue;
        case '\v': stream << L"\\v"; continue;
        case '\a': stream << L"\\a"; continue;
        case '\\': stream << L"\\\\"; continue;
        case '\"': stream << L"\\\""; continue;
        case '\'': stream << L"\\\'"; continue;
        }
        stream << ch;
    }
    std::wstring str = stream.str();
}
inline void DisplayError(
const MString& msg)
 
{
    
}
{
    
    DisplayError(msg);
}
{
    
    msg.
format(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
    DisplayError(msg);
}
inline void DisplayWarning(
const MString& msg)
 
{
    
}
{
    
    DisplayWarning(msg);
}
{
    
    msg.
format(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
    DisplayWarning(msg);
}
} 
#endif