#include "SceneCache.h"
namespace
{
    const float ANGLE_TO_RADIAN = 3.1415926f / 180.f;
    const GLfloat BLACK_COLOR[] = {0.0f, 0.0f, 0.0f, 1.0f};
    const GLfloat GREEN_COLOR[] = {0.0f, 1.0f, 0.0f, 1.0f};
    const GLfloat WHITE_COLOR[] = {1.0f, 1.0f, 1.0f, 1.0f};
    const GLfloat WIREFRAME_COLOR[] = {0.5f, 0.5f, 0.5f, 1.0f};
    const int TRIANGLE_VERTEX_COUNT = 3;
    
    const int VERTEX_STRIDE = 4;
    
    const int NORMAL_STRIDE = 3;
    
    const int UV_STRIDE = 2;
    const GLfloat DEFAULT_LIGHT_POSITION[] = {0.0f, 0.0f, 0.0f, 1.0f};
    const GLfloat DEFAULT_DIRECTION_LIGHT_POSITION[] = {0.0f, 0.0f, 1.0f, 0.0f};
    const GLfloat DEFAULT_SPOT_LIGHT_DIRECTION[] = {0.0f, 0.0f, -1.0f};
    const GLfloat DEFAULT_LIGHT_COLOR[] = {1.0f, 1.0f, 1.0f, 1.0f};
    const GLfloat DEFAULT_LIGHT_SPOT_CUTOFF = 180.0f;
    
    
        const char * pPropertyName,
        const char * pFactorPropertyName,
        GLuint & pTextureName)
    {
        {
            if (lFactor != 1)
            {
                lResult[0] *= lFactor;
                lResult[1] *= lFactor;
                lResult[2] *= lFactor;
            }
        }
        {
            if (lTextureCount)
            {
                {
                }
            }
        }
        return lResult;
    }
}
VBOMesh::VBOMesh() : mHasNormal(false), mHasUV(false), mAllByControlPoint(true)
{
    
    for (int lVBOIndex = 0; lVBOIndex < VBO_COUNT; ++lVBOIndex)
    {
        mVBONames[lVBOIndex] = 0;
    }
}
VBOMesh::~VBOMesh()
{
    
    glDeleteBuffers(VBO_COUNT, mVBONames);
    
    for(int i=0; i < mSubMeshes.GetCount(); i++)
    {
        delete mSubMeshes[i];
    }
    
    mSubMeshes.Clear();
}
bool VBOMesh::Initialize(
const FbxMesh *pMesh)
 
{
        return false;
    
    {
        {
            FBX_ASSERT(lMaterialIndice->
GetCount() == lPolygonCount);
 
            if (lMaterialIndice->
GetCount() == lPolygonCount)
 
            {
                
                for (int lPolygonIndex = 0; lPolygonIndex < lPolygonCount; ++lPolygonIndex)
                {
                    const int lMaterialIndex = lMaterialIndice->
GetAt(lPolygonIndex);
 
                    if (mSubMeshes.GetCount() < lMaterialIndex + 1)
                    {
                        mSubMeshes.Resize(lMaterialIndex + 1);
                    }
                    if (mSubMeshes[lMaterialIndex] == 
NULL)
 
                    {
                        mSubMeshes[lMaterialIndex] = new SubMesh;
                    }
                    mSubMeshes[lMaterialIndex]->TriangleCount += 1;
                }
                
                
                for (int i = 0; i < mSubMeshes.GetCount(); i++)
                {
                    if (mSubMeshes[i] == 
NULL)
 
                        mSubMeshes[i] = new SubMesh;
                }
                
                const int lMaterialCount = mSubMeshes.GetCount();
                int lOffset = 0;
                for (int lIndex = 0; lIndex < lMaterialCount; ++lIndex)
                {
                    mSubMeshes[lIndex]->IndexOffset = lOffset;
                    lOffset += mSubMeshes[lIndex]->TriangleCount * 3;
                    
                    mSubMeshes[lIndex]->TriangleCount = 0;
                }
                FBX_ASSERT(lOffset == lPolygonCount * 3);
            }
        }
    }
    
    if (mSubMeshes.GetCount() == 0)
    {
        mSubMeshes.Resize(1);
        mSubMeshes[0] = new SubMesh();
    }
    
    
    if (mHasNormal)
    {
        {
            mHasNormal = false;
        }
        {
            mAllByControlPoint = false;
        }
    }
    if (mHasUV)
    {
        {
            mHasUV = false;
        }
        {
            mAllByControlPoint = false;
        }
    }
    
    if (!mAllByControlPoint)
    {
        lPolygonVertexCount = lPolygonCount * TRIANGLE_VERTEX_COUNT;
    }
    float * lVertices = new float[lPolygonVertexCount * VERTEX_STRIDE];
    unsigned int * lIndices = new unsigned int[lPolygonCount * TRIANGLE_VERTEX_COUNT];
    if (mHasNormal)
    {
        lNormals = new float[lPolygonVertexCount * NORMAL_STRIDE];
    }
    const char * lUVName = 
NULL;
 
    {
        lUVs = new float[lPolygonVertexCount * UV_STRIDE];
        lUVName = lUVNames[0];
    }
    
    if (mAllByControlPoint)
    {
        if (mHasNormal)
        {
        }
        if (mHasUV)
        {
        }
        for (int lIndex = 0; lIndex < lPolygonVertexCount; ++lIndex)
        {
            
            lCurrentVertex = lControlPoints[lIndex];
            lVertices[lIndex * VERTEX_STRIDE] = static_cast<float>(lCurrentVertex[0]);
            lVertices[lIndex * VERTEX_STRIDE + 1] = static_cast<float>(lCurrentVertex[1]);
            lVertices[lIndex * VERTEX_STRIDE + 2] = static_cast<float>(lCurrentVertex[2]);
            lVertices[lIndex * VERTEX_STRIDE + 3] = 1;
            
            if (mHasNormal)
            {
                int lNormalIndex = lIndex;
                {
                }
                lCurrentNormal = lNormalElement->
GetDirectArray().GetAt(lNormalIndex);
 
                lNormals[lIndex * NORMAL_STRIDE] = static_cast<float>(lCurrentNormal[0]);
                lNormals[lIndex * NORMAL_STRIDE + 1] = static_cast<float>(lCurrentNormal[1]);
                lNormals[lIndex * NORMAL_STRIDE + 2] = static_cast<float>(lCurrentNormal[2]);
            }
            
            if (mHasUV)
            {
                int lUVIndex = lIndex;
                {
                }
                lUVs[lIndex * UV_STRIDE] = static_cast<float>(lCurrentUV[0]);
                lUVs[lIndex * UV_STRIDE + 1] = static_cast<float>(lCurrentUV[1]);
            }
        }
    }
    int lVertexCount = 0;
    for (int lPolygonIndex = 0; lPolygonIndex < lPolygonCount; ++lPolygonIndex)
    {
        
        int lMaterialIndex = 0;
        {
            lMaterialIndex = lMaterialIndice->
GetAt(lPolygonIndex);
 
        }
        
        const int lIndexOffset = mSubMeshes[lMaterialIndex]->IndexOffset +
            mSubMeshes[lMaterialIndex]->TriangleCount * 3;
        for (int lVerticeIndex = 0; lVerticeIndex < TRIANGLE_VERTEX_COUNT; ++lVerticeIndex)
        {
            const int lControlPointIndex = pMesh->
GetPolygonVertex(lPolygonIndex, lVerticeIndex);
 
            if (mAllByControlPoint)
            {
                lIndices[lIndexOffset + lVerticeIndex] = static_cast<unsigned int>(lControlPointIndex);
            }
            
            else
            {
                lIndices[lIndexOffset + lVerticeIndex] = static_cast<unsigned int>(lVertexCount);
                lCurrentVertex = lControlPoints[lControlPointIndex];
                lVertices[lVertexCount * VERTEX_STRIDE] = static_cast<float>(lCurrentVertex[0]);
                lVertices[lVertexCount * VERTEX_STRIDE + 1] = static_cast<float>(lCurrentVertex[1]);
                lVertices[lVertexCount * VERTEX_STRIDE + 2] = static_cast<float>(lCurrentVertex[2]);
                lVertices[lVertexCount * VERTEX_STRIDE + 3] = 1;
                if (mHasNormal)
                {
                    lNormals[lVertexCount * NORMAL_STRIDE] = static_cast<float>(lCurrentNormal[0]);
                    lNormals[lVertexCount * NORMAL_STRIDE + 1] = static_cast<float>(lCurrentNormal[1]);
                    lNormals[lVertexCount * NORMAL_STRIDE + 2] = static_cast<float>(lCurrentNormal[2]);
                }
                if (mHasUV)
                {
                    bool lUnmappedUV;
                    pMesh->
GetPolygonVertexUV(lPolygonIndex, lVerticeIndex, lUVName, lCurrentUV, lUnmappedUV);
 
                    lUVs[lVertexCount * UV_STRIDE] = static_cast<float>(lCurrentUV[0]);
                    lUVs[lVertexCount * UV_STRIDE + 1] = static_cast<float>(lCurrentUV[1]);
                }
            }
            ++lVertexCount;
        }
        mSubMeshes[lMaterialIndex]->TriangleCount += 1;
    }
    
    glGenBuffers(VBO_COUNT, mVBONames);
    
    glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
    glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * VERTEX_STRIDE * sizeof(float), lVertices, GL_STATIC_DRAW);
    delete [] lVertices;
    if (mHasNormal)
    {
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[NORMAL_VBO]);
        glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * NORMAL_STRIDE * sizeof(float), lNormals, GL_STATIC_DRAW);
        delete [] lNormals;
    }
    
    if (mHasUV)
    {
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[UV_VBO]);
        glBufferData(GL_ARRAY_BUFFER, lPolygonVertexCount * UV_STRIDE * sizeof(float), lUVs, GL_STATIC_DRAW);
        delete [] lUVs;
    }
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBONames[INDEX_VBO]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, lPolygonCount * TRIANGLE_VERTEX_COUNT * sizeof(unsigned int), lIndices, GL_STATIC_DRAW);
    delete [] lIndices;
    return true;
}
void VBOMesh::UpdateVertexPosition(
const FbxMesh * pMesh, 
const FbxVector4 * pVertices)
 const 
{
    
    float * lVertices = 
NULL;
 
    int lVertexCount = 0;
    if (mAllByControlPoint)
    {
        lVertices = new float[lVertexCount * VERTEX_STRIDE];
        for (int lIndex = 0; lIndex < lVertexCount; ++lIndex)
        {
            lVertices[lIndex * VERTEX_STRIDE] = static_cast<float>(pVertices[lIndex][0]);
            lVertices[lIndex * VERTEX_STRIDE + 1] = static_cast<float>(pVertices[lIndex][1]);
            lVertices[lIndex * VERTEX_STRIDE + 2] = static_cast<float>(pVertices[lIndex][2]);
            lVertices[lIndex * VERTEX_STRIDE + 3] = 1;
        }
    }
    else
    {
        lVertexCount = lPolygonCount * TRIANGLE_VERTEX_COUNT;
        lVertices = new float[lVertexCount * VERTEX_STRIDE];
        lVertexCount = 0;
        for (int lPolygonIndex = 0; lPolygonIndex < lPolygonCount; ++lPolygonIndex)
        {
            for (int lVerticeIndex = 0; lVerticeIndex < TRIANGLE_VERTEX_COUNT; ++lVerticeIndex)
            {
                const int lControlPointIndex = pMesh->
GetPolygonVertex(lPolygonIndex, lVerticeIndex);
 
                lVertices[lVertexCount * VERTEX_STRIDE] = static_cast<float>(pVertices[lControlPointIndex][0]);
                lVertices[lVertexCount * VERTEX_STRIDE + 1] = static_cast<float>(pVertices[lControlPointIndex][1]);
                lVertices[lVertexCount * VERTEX_STRIDE + 2] = static_cast<float>(pVertices[lControlPointIndex][2]);
                lVertices[lVertexCount * VERTEX_STRIDE + 3] = 1;
                ++lVertexCount;
            }
        }
    }
    
    if (lVertices)
    {
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
        glBufferData(GL_ARRAY_BUFFER, lVertexCount * VERTEX_STRIDE * sizeof(float), lVertices, GL_STATIC_DRAW);
        delete [] lVertices;
    }
}
void VBOMesh::Draw(int pMaterialIndex, ShadingMode pShadingMode) const
{
#if _MSC_VER >= 1900 && defined(_WIN64)
    
    #pragma warning( push )
    #pragma warning( disable : 4312)
#endif
    
    GLsizei lOffset = mSubMeshes[pMaterialIndex]->IndexOffset * sizeof(unsigned int);
    if ( pShadingMode == SHADING_MODE_SHADED)
    {
        const GLsizei lElementCount = mSubMeshes[pMaterialIndex]->TriangleCount * 3;
        glDrawElements(GL_TRIANGLES, lElementCount, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(lOffset));
    }
    else
    {
        for (int lIndex = 0; lIndex < mSubMeshes[pMaterialIndex]->TriangleCount; ++lIndex)
        {
            
            glDrawElements(GL_LINE_LOOP, TRIANGLE_VERTEX_COUNT, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(lOffset));
            lOffset += sizeof(unsigned int) * TRIANGLE_VERTEX_COUNT;
        }
    }
#if _MSC_VER >= 1900 && defined(_WIN64)
    #pragma warning( pop )
#endif
}
void VBOMesh::BeginDraw(ShadingMode pShadingMode) const
{
    
    glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);
    glPushAttrib(GL_ENABLE_BIT);
    glPushAttrib(GL_CURRENT_BIT);
    glPushAttrib(GL_LIGHTING_BIT);
    glPushAttrib(GL_TEXTURE_BIT);
    
    glBindBuffer(GL_ARRAY_BUFFER, mVBONames[VERTEX_VBO]);
    glVertexPointer(VERTEX_STRIDE, GL_FLOAT, 0, 0);
    glEnableClientState(GL_VERTEX_ARRAY);
    
    if (mHasNormal && pShadingMode == SHADING_MODE_SHADED)
    {
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[NORMAL_VBO]);
        glNormalPointer(GL_FLOAT, 0, 0);
        glEnableClientState(GL_NORMAL_ARRAY);
    }
    
    
    if (mHasUV && pShadingMode == SHADING_MODE_SHADED)
    {
        glBindBuffer(GL_ARRAY_BUFFER, mVBONames[UV_VBO]);
        glTexCoordPointer(UV_STRIDE, GL_FLOAT, 0, 0);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    }
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBONames[INDEX_VBO]);
    if (pShadingMode == SHADING_MODE_SHADED)
    {
        glEnable(GL_LIGHTING);
        glEnable(GL_TEXTURE_2D);
        glEnable(GL_NORMALIZE);
    }
    else
    {
        glColor4fv(WIREFRAME_COLOR);
    }
}
void VBOMesh::EndDraw() const
{
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    glPopAttrib();
    glPopAttrib();
    glPopAttrib();
    glPopAttrib();
    glPopClientAttrib();
}
MaterialCache::MaterialCache() : mShinness(0)
{
}
MaterialCache::~MaterialCache()
{
}
{
    const FbxDouble3 lEmissive = GetMaterialProperty(pMaterial,
 
    mEmissive.mColor[0] = static_cast<GLfloat>(lEmissive[0]);
    mEmissive.mColor[1] = static_cast<GLfloat>(lEmissive[1]);
    mEmissive.mColor[2] = static_cast<GLfloat>(lEmissive[2]);
    const FbxDouble3 lAmbient = GetMaterialProperty(pMaterial,
 
    mAmbient.mColor[0] = static_cast<GLfloat>(lAmbient[0]);
    mAmbient.mColor[1] = static_cast<GLfloat>(lAmbient[1]);
    mAmbient.mColor[2] = static_cast<GLfloat>(lAmbient[2]);
    const FbxDouble3 lDiffuse = GetMaterialProperty(pMaterial,
 
    mDiffuse.mColor[0] = static_cast<GLfloat>(lDiffuse[0]);
    mDiffuse.mColor[1] = static_cast<GLfloat>(lDiffuse[1]);
    mDiffuse.mColor[2] = static_cast<GLfloat>(lDiffuse[2]);
    const FbxDouble3 lSpecular = GetMaterialProperty(pMaterial,
 
    mSpecular.mColor[0] = static_cast<GLfloat>(lSpecular[0]);
    mSpecular.mColor[1] = static_cast<GLfloat>(lSpecular[1]);
    mSpecular.mColor[2] = static_cast<GLfloat>(lSpecular[2]);
    {
        mShinness = static_cast<GLfloat>(lShininess);
    }
    return true;
}
void MaterialCache::SetCurrentMaterial() const
{
    glMaterialfv(GL_FRONT, GL_EMISSION, mEmissive.mColor);
    glMaterialfv(GL_FRONT, GL_AMBIENT, mAmbient.mColor);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mDiffuse.mColor);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mSpecular.mColor);
    glMaterialf(GL_FRONT, GL_SHININESS, mShinness);
    glBindTexture(GL_TEXTURE_2D, mDiffuse.mTextureName);
}
void MaterialCache::SetDefaultMaterial()
{
    glMaterialfv(GL_FRONT, GL_EMISSION, BLACK_COLOR);
    glMaterialfv(GL_FRONT, GL_AMBIENT, BLACK_COLOR);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, GREEN_COLOR);
    glMaterialfv(GL_FRONT, GL_SPECULAR, BLACK_COLOR);
    glMaterialf(GL_FRONT, GL_SHININESS, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
}
int LightCache::sLightCount = 0;
LightCache::LightCache() : mType(
FbxLight::ePoint)
 
{
    mLightIndex = GL_LIGHT0 + sLightCount++;
}
LightCache::~LightCache()
{
    glDisable(mLightIndex);
    --sLightCount;
}
{
    mColorRed.mValue = static_cast<float>(lLightColor[0]);
    mColorGreen.mValue = static_cast<float>(lLightColor[1]);
    mColorBlue.mValue = static_cast<float>(lLightColor[2]);
    if (pAnimLayer)
    {
    }
    {
        mConeAngle.mValue = 
static_cast<GLfloat
>(lConeAngleProperty.
Get());
 
        if (pAnimLayer)
            mConeAngle.mAnimCurve = lConeAngleProperty.
GetCurve(pAnimLayer);
 
    }
    return true;
}
void LightCache::SetLight(
const FbxTime & pTime)
 const 
{
    const GLfloat lLightColor[4] = {mColorRed.Get(pTime), mColorGreen.Get(pTime), mColorBlue.Get(pTime), 1.0f};
    const GLfloat lConeAngle = mConeAngle.Get(pTime);
    glColor3fv(lLightColor);
    glPushAttrib(GL_ENABLE_BIT);
    glPushAttrib(GL_POLYGON_BIT);
    
    glDisable(GL_CULL_FACE);
    
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    {
        
        glPushMatrix();
        glScalef(1.0f, 1.0f, -1.0f);
        const double lRadians = ANGLE_TO_RADIAN * lConeAngle;
        const double lHeight = 15.0;
        const double lBase = lHeight * tan(lRadians / 2);
        GLUquadricObj * lQuadObj = gluNewQuadric();
        gluCylinder(lQuadObj, 0.0, lBase, lHeight, 18, 1);
        gluDeleteQuadric(lQuadObj);
        glPopMatrix();
    }
    else
    {
        
        GLUquadricObj * lQuadObj = gluNewQuadric();
        gluSphere(lQuadObj, 1.0, 10, 10);
        gluDeleteQuadric(lQuadObj);
    }
    glPopAttrib();
    glPopAttrib();
    
    {
        glLightfv(mLightIndex, GL_POSITION, DEFAULT_DIRECTION_LIGHT_POSITION);
    }
    else
    {
        glLightfv(mLightIndex, GL_POSITION, DEFAULT_LIGHT_POSITION);
    }
    glLightfv(mLightIndex, GL_DIFFUSE, lLightColor);
    glLightfv(mLightIndex, GL_SPECULAR, lLightColor);
    
    {
        glLightfv(mLightIndex, GL_SPOT_DIRECTION, DEFAULT_SPOT_LIGHT_DIRECTION);
        
        if (lConeAngle != 0.0f)
        {
            
            glLightf(mLightIndex, GL_SPOT_CUTOFF, lConeAngle/2);
        }
    }
    glEnable(mLightIndex);
}
void LightCache::IntializeEnvironment(
const FbxColor & pAmbientLight)
 
{
    glLightfv(GL_LIGHT0, GL_POSITION, DEFAULT_DIRECTION_LIGHT_POSITION);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, DEFAULT_LIGHT_COLOR);
    glLightfv(GL_LIGHT0, GL_SPECULAR, DEFAULT_LIGHT_COLOR);
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, DEFAULT_LIGHT_SPOT_CUTOFF);
    glEnable(GL_LIGHT0);
    
    GLfloat lAmbientLight[] = {static_cast<GLfloat>(pAmbientLight[0]), static_cast<GLfloat>(pAmbientLight[1]),
        static_cast<GLfloat>(pAmbientLight[2]), 1.0f};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lAmbientLight);
}