Areloch 6 years ago
parent
commit
000c7b2263

+ 228 - 1
Engine/source/gfx/gl/gfxGLCubemap.cpp

@@ -31,7 +31,7 @@
 #include "gfx/bitmap/imageUtils.h"
 #include "gfx/bitmap/imageUtils.h"
 
 
 
 
-GLenum GFXGLCubemap::faceList[6] = 
+static GLenum faceList[6] = 
 { 
 { 
    GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
    GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
    GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
    GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
@@ -55,6 +55,11 @@ GFXGLCubemap::~GFXGLCubemap()
    GFXTextureManager::removeEventDelegate( this, &GFXGLCubemap::_onTextureEvent );
    GFXTextureManager::removeEventDelegate( this, &GFXGLCubemap::_onTextureEvent );
 }
 }
 
 
+GLenum GFXGLCubemap::getEnumForFaceNumber(U32 face)
+{ 
+   return faceList[face];
+}
+
 void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces)
 void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces)
 {
 {
    AssertFatal( faces, "");
    AssertFatal( faces, "");
@@ -280,3 +285,225 @@ void GFXGLCubemap::_onTextureEvent( GFXTexCallbackCode code )
    else
    else
       tmResurrect();
       tmResurrect();
 }
 }
+
+U8* GFXGLCubemap::getTextureData(U32 face, U32 mip)
+{
+   AssertFatal(mMipMapLevels, "");
+   mip = (mip < mMipMapLevels) ? mip : 0;
+   const U32 bytesPerTexel = 4; //TODO make work with more formats!!!!!
+   const U32 dataSize = ImageUtil::isCompressedFormat(mFaceFormat)
+      ? getCompressedSurfaceSize(mFaceFormat, mWidth, mHeight, mip)
+      : (mWidth >> mip) * (mHeight >> mip) * bytesPerTexel;
+
+   U8* data = new U8[dataSize];
+   PRESERVE_TEXTURE(GL_TEXTURE_CUBE_MAP);
+   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
+
+   if (ImageUtil::isCompressedFormat(mFaceFormat))
+      glGetCompressedTexImage(faceList[face], mip, data);
+   else
+      glGetTexImage(faceList[face], mip, GFXGLTextureFormat[mFaceFormat], GFXGLTextureType[mFaceFormat], data);
+
+   return data;
+}
+
+//-----------------------------------------------------------------------------
+// Cubemap Array
+//-----------------------------------------------------------------------------
+
+GFXGLCubemapArray::GFXGLCubemapArray()
+{
+}
+
+GFXGLCubemapArray::~GFXGLCubemapArray()
+{
+   glDeleteTextures(1, &mCubemap);
+}
+
+//TODO: really need a common private 'init' function to avoid code double up with these init* functions
+void GFXGLCubemapArray::init(GFXCubemapHandle *cubemaps, const U32 cubemapCount)
+{
+   AssertFatal(cubemaps, "GFXGLCubemapArray- Got null GFXCubemapHandle!");
+   AssertFatal(*cubemaps, "GFXGLCubemapArray - Got empty cubemap!");
+
+   //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap
+   mSize = cubemaps[0]->getSize();
+   mFormat = cubemaps[0]->getFormat();
+   mMipMapLevels = cubemaps[0]->getMipMapLevels();
+   mNumCubemaps = cubemapCount;
+   const bool isCompressed = ImageUtil::isCompressedFormat(mFormat);
+
+   glGenTextures(1, &mCubemap);
+   PRESERVE_CUBEMAP_ARRAY_TEXTURE();
+   glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+   for (U32 i = 0; i < cubemapCount; i++)
+   {
+      GFXGLCubemap* glTex = static_cast<GFXGLCubemap*>(cubemaps[i].getPointer());
+      for (U32 face = 0; face < 6; face++)
+      {
+         for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+         //U32 currentMip = 0;
+         {
+            U8 *pixelData = glTex->getTextureData(face, currentMip);
+            const U32 mipSize = getMax(U32(1), mSize >> currentMip);
+            /*if (isCompressed)
+            {
+               const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip);
+               glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData);
+            }
+            else
+            {*/                                                      //TODO figure out xyzOffsets
+            glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, currentMip, 0, 0, 0, mipSize, mipSize, i * face, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData);
+            //}
+            delete[] pixelData;
+         }
+      }
+   }
+}
+
+//Just allocate the cubemap array but we don't upload any data
+void GFXGLCubemapArray::init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format)
+{
+   //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap
+   mSize = cubemapFaceSize;
+   mFormat = format;
+   mMipMapLevels = ImageUtil::getMaxMipCount(cubemapFaceSize, cubemapFaceSize);
+   mNumCubemaps = cubemapCount;
+   const bool isCompressed = ImageUtil::isCompressedFormat(mFormat);
+
+   glGenTextures(1, &mCubemap);
+   PRESERVE_CUBEMAP_ARRAY_TEXTURE();
+   glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+   /*for (U32 i = 0; i < cubemapCount; i++)
+   {
+      GFXGLCubemap* glTex = static_cast<GFXGLCubemap*>(cubemaps[i].getPointer());
+      for (U32 face = 0; face < 6; face++)
+      {
+         for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+         {
+            U8 *pixelData = glTex->getTextureData(face, currentMip);
+            const U32 mipSize = getMax(U32(1), mSize >> currentMip);
+            if (isCompressed)
+            {
+               const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip);
+               glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData);
+            }
+            else
+            {
+               glTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize,
+                  0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData);
+            }
+            delete[] pixelData;
+         }
+      }
+   }*/
+}
+
+void GFXGLCubemapArray::updateTexture(const GFXCubemapHandle &cubemap, const U32 slot)
+{
+   AssertFatal(slot <= mNumCubemaps, "GFXD3D11CubemapArray::updateTexture - trying to update a cubemap texture that is out of bounds!");
+
+   const bool isCompressed = ImageUtil::isCompressedFormat(mFormat);
+
+   GFXGLCubemap* glTex = static_cast<GFXGLCubemap*>(cubemap.getPointer());
+   for (U32 face = 0; face < 6; face++)
+   {
+      for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+      //U32 currentMip = 0;
+      {
+         U8 *pixelData = glTex->getTextureData(face, currentMip);
+         const U32 mipSize = getMax(U32(1), mSize >> currentMip);
+         /*if (isCompressed)
+         {
+            const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip);
+            glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData);
+         }
+         else
+         {*/                                                      //TODO figure out xyzOffsets
+         glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, currentMip, 0, 0, 0, mipSize, mipSize, slot * face, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData);
+         //}
+         delete[] pixelData;
+      }
+   }
+}
+
+void GFXGLCubemapArray::copyTo(GFXCubemapArray *pDstCubemap)
+{
+   AssertFatal(pDstCubemap, "GFXGLCubemapArray::copyTo - Got null GFXCubemapArray");
+
+   const U32 dstCount = pDstCubemap->getNumCubemaps();
+   const GFXFormat dstFmt = pDstCubemap->getFormat();
+   const U32 dstSize = pDstCubemap->getSize();
+   const U32 dstMips = pDstCubemap->getMipMapLevels();
+
+   AssertFatal(dstCount > mNumCubemaps, "GFXGLCubemapArray::copyTo - Destination too small");
+   AssertFatal(dstFmt == mFormat, "GFXGLCubemapArray::copyTo - Destination format doesn't match");
+   AssertFatal(dstSize == mSize, "GFXGLCubemapArray::copyTo - Destination size doesn't match");
+   AssertFatal(dstMips == mMipMapLevels, "GFXGLCubemapArray::copyTo - Destination mip levels doesn't match");
+
+   GFXGLCubemapArray* pDstCube = static_cast<GFXGLCubemapArray*>(pDstCubemap);
+
+   for (U32 cubeMap = 0; cubeMap < mNumCubemaps; cubeMap++)
+   {
+      for (U32 face = 0; face < CubeFaces; face++)
+      {
+         for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+         //U32 currentMip = 0;
+         {
+            //U8 *pixelData = pDstCube->get->getTextureData(face, currentMip);
+            const U32 mipSize = getMax(U32(1), mSize >> currentMip);
+            /*if (isCompressed)
+            {
+               const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip);
+               glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData);
+            }
+            else
+            {*/                                                      //TODO figure out xyzOffsets
+            glCopyImageSubData(mCubemap, GL_TEXTURE_CUBE_MAP_ARRAY, cubeMap * face, 0, 0, 0, pDstCube->mCubemap, GL_TEXTURE_CUBE_MAP_ARRAY, cubeMap * face, 0, 0, 0, mipSize, mipSize, 6);
+            //glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+            //glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY, currentMip, 0, 0, 0, mipSize, mipSize, CubeFaces, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData);
+            //}
+            //delete[] pixelData;
+         }
+      }
+   }
+}
+
+
+void GFXGLCubemapArray::setToTexUnit(U32 tuNum)
+{
+   static_cast<GFXGLDevice*>(getOwningDevice())->setCubemapArrayInternal(tuNum, this);
+}
+
+void GFXGLCubemapArray::bind(U32 textureUnit) const
+{
+   glActiveTexture(GL_TEXTURE0 + textureUnit);
+   glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+   static_cast<GFXGLDevice*>(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+
+   GFXGLStateBlockRef sb = static_cast<GFXGLDevice*>(GFX)->getCurrentStateBlock();
+   AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!");
+   if (!sb)
+      return;
+
+   const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit];
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0));
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]);
+}

+ 31 - 2
Engine/source/gfx/gl/gfxGLCubemap.h

@@ -30,6 +30,9 @@
 #include "core/resource.h"
 #include "core/resource.h"
 #endif
 #endif
 
 
+const U32 CubeFaces = 6;
+const U32 MaxMipMaps = 13; //todo this needs a proper static value somewhere to sync up with other classes like GBitmap
+
 
 
 class GFXGLCubemap : public GFXCubemap
 class GFXGLCubemap : public GFXCubemap
 {
 {
@@ -55,7 +58,11 @@ public:
    /// Called by texCB; this is to ensure that all textures have been resurrected before we attempt to res the cubemap.
    /// Called by texCB; this is to ensure that all textures have been resurrected before we attempt to res the cubemap.
    void tmResurrect();
    void tmResurrect();
    
    
-   static GLenum getEnumForFaceNumber(U32 face) { return faceList[face]; } ///< Performs lookup to get a GLenum for the given face number
+   static GLenum getEnumForFaceNumber(U32 face);///< Performs lookup to get a GLenum for the given face number
+
+   /// @return An array containing the texture data
+   /// @note You are responsible for deleting the returned data! (Use delete[])
+   U8* getTextureData(U32 face, U32 mip = 0);
 
 
 protected:
 protected:
 
 
@@ -85,7 +92,29 @@ protected:
    virtual void bind(U32 textureUnit) const; ///< Notifies our owning device that we want to be set to the given texture unit (used for GL internal state tracking)
    virtual void bind(U32 textureUnit) const; ///< Notifies our owning device that we want to be set to the given texture unit (used for GL internal state tracking)
    void fillCubeTextures(GFXTexHandle* faces); ///< Copies the textures in faces into the cubemap
    void fillCubeTextures(GFXTexHandle* faces); ///< Copies the textures in faces into the cubemap
    
    
-   static GLenum faceList[6]; ///< Lookup table
+};
+
+class GFXGLCubemapArray : public GFXCubemapArray
+{
+public:
+   GFXGLCubemapArray();
+   virtual ~GFXGLCubemapArray();
+   //virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount);
+   virtual void init(GFXCubemapHandle *cubemaps, const U32 cubemapCount);
+   virtual void init(const U32 cubemapCount, const U32 cubemapFaceSize, const GFXFormat format);
+   virtual void updateTexture(const GFXCubemapHandle &cubemap, const U32 slot);
+   virtual void copyTo(GFXCubemapArray *pDstCubemap);
+   virtual void setToTexUnit(U32 tuNum);
+
+   // GFXResource interface
+   virtual void zombify() {}
+   virtual void resurrect() {}
+
+protected:
+   friend class GFXGLDevice;
+   void bind(U32 textureUnit) const;
+   GLuint mCubemap; ///< Internal GL handle
+
 };
 };
 
 
 #endif
 #endif

+ 23 - 0
Engine/source/gfx/gl/gfxGLDevice.cpp

@@ -448,6 +448,13 @@ GFXCubemap* GFXGLDevice::createCubemap()
    return cube; 
    return cube; 
 };
 };
 
 
+GFXCubemapArray *GFXGLDevice::createCubemapArray()
+{
+   GFXGLCubemapArray* cubeArray = new GFXGLCubemapArray();
+   cubeArray->registerResourceWithDevice(this);
+   return cubeArray;
+}
+
 void GFXGLDevice::endSceneInternal() 
 void GFXGLDevice::endSceneInternal() 
 {
 {
    // nothing to do for opengl
    // nothing to do for opengl
@@ -675,6 +682,22 @@ void GFXGLDevice::setCubemapInternal(U32 textureUnit, const GFXGLCubemap* textur
    }
    }
 }
 }
 
 
+void GFXGLDevice::setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture)
+{
+   if (texture)
+   {
+      mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP_ARRAY;
+      texture->bind(textureUnit);
+   }
+   else if (mActiveTextureType[textureUnit] != GL_ZERO)
+   {
+      glActiveTexture(GL_TEXTURE0 + textureUnit);
+      glBindTexture(mActiveTextureType[textureUnit], 0);
+      getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0);
+      mActiveTextureType[textureUnit] = GL_ZERO;
+   }
+}
+
 void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat )
 void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat )
 {
 {
    // ONLY NEEDED ON FFP
    // ONLY NEEDED ON FFP

+ 6 - 3
Engine/source/gfx/gl/gfxGLDevice.h

@@ -39,6 +39,7 @@ class GFXGLVertexBuffer;
 class GFXGLPrimitiveBuffer;
 class GFXGLPrimitiveBuffer;
 class GFXGLTextureTarget;
 class GFXGLTextureTarget;
 class GFXGLCubemap;
 class GFXGLCubemap;
+class GFXGLCubemapArray;
 class GFXGLStateCache;
 class GFXGLStateCache;
 class GFXGLVertexDecl;
 class GFXGLVertexDecl;
 
 
@@ -81,7 +82,7 @@ public:
    virtual U32 getTotalVideoMemory();
    virtual U32 getTotalVideoMemory();
 
 
    virtual GFXCubemap * createCubemap();
    virtual GFXCubemap * createCubemap();
-   virtual GFXCubemapArray *createCubemapArray() { return NULL; } //TODO: implement
+   virtual GFXCubemapArray *createCubemapArray();
 
 
    virtual F32 getFillConventionOffset() const { return 0.0f; }
    virtual F32 getFillConventionOffset() const { return 0.0f; }
 
 
@@ -170,7 +171,8 @@ protected:
    virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer);
    virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer);
 
 
    virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture);
    virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture);
-   virtual void setCubemapInternal(U32 cubemap, const GFXGLCubemap* texture);
+   virtual void setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture);
+   virtual void setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture);
 
 
    virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable);
    virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable);
    virtual void setLightMaterialInternal(const GFXLightMaterial mat);
    virtual void setLightMaterialInternal(const GFXLightMaterial mat);
@@ -207,11 +209,12 @@ private:
    
    
    friend class GFXGLTextureObject;
    friend class GFXGLTextureObject;
    friend class GFXGLCubemap;
    friend class GFXGLCubemap;
+   friend class GFXGLCubemapArray;
    friend class GFXGLWindowTarget;
    friend class GFXGLWindowTarget;
    friend class GFXGLPrimitiveBuffer;
    friend class GFXGLPrimitiveBuffer;
    friend class GFXGLVertexBuffer;
    friend class GFXGLVertexBuffer;
 
 
-   static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; 
+   static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance;
 
 
    U32 mAdapterIndex;
    U32 mAdapterIndex;
    
    

+ 7 - 2
Engine/source/gfx/gl/gfxGLShader.cpp

@@ -80,6 +80,7 @@ static U32 shaderConstTypeSize(GFXShaderConstType type)
    case GFXSCT_Int:
    case GFXSCT_Int:
    case GFXSCT_Sampler:
    case GFXSCT_Sampler:
    case GFXSCT_SamplerCube:
    case GFXSCT_SamplerCube:
+   case GFXSCT_SamplerCubeArray:
       return 4;
       return 4;
    case GFXSCT_Float2:
    case GFXSCT_Float2:
    case GFXSCT_Int2:
    case GFXSCT_Int2:
@@ -625,6 +626,9 @@ void GFXGLShader::initConstantDescs()
          case GL_SAMPLER_CUBE:
          case GL_SAMPLER_CUBE:
             desc.constType = GFXSCT_SamplerCube;
             desc.constType = GFXSCT_SamplerCube;
             break;
             break;
+         case GL_SAMPLER_CUBE_MAP_ARRAY:
+            desc.constType = GFXSCT_SamplerCubeArray;
+            break;
          default:
          default:
             AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type");
             AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type");
             // If we don't recognize the constant don't add its description.
             // If we don't recognize the constant don't add its description.
@@ -656,7 +660,7 @@ void GFXGLShader::initHandles()
 
 
       HandleMap::Iterator handle = mHandles.find(desc.name);
       HandleMap::Iterator handle = mHandles.find(desc.name);
       S32 sampler = -1;
       S32 sampler = -1;
-      if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube)
+      if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube || desc.constType == GFXSCT_SamplerCubeArray)
       {
       {
          S32 idx = mSamplerNamesOrdered.find_next(desc.name);
          S32 idx = mSamplerNamesOrdered.find_next(desc.name);
          AssertFatal(idx != -1, "");
          AssertFatal(idx != -1, "");
@@ -699,7 +703,7 @@ void GFXGLShader::initHandles()
    for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter)
    for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter)
    {
    {
       GFXGLShaderConstHandle* handle = iter->value;
       GFXGLShaderConstHandle* handle = iter->value;
-      if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube))
+      if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube || handle->getType() == GFXSCT_SamplerCubeArray))
       {
       {
          // Set sampler number on our program.
          // Set sampler number on our program.
          glUniform1i(handle->mLocation, handle->mSamplerNum);
          glUniform1i(handle->mLocation, handle->mSamplerNum);
@@ -831,6 +835,7 @@ void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer)
          case GFXSCT_Int:
          case GFXSCT_Int:
          case GFXSCT_Sampler:
          case GFXSCT_Sampler:
          case GFXSCT_SamplerCube:
          case GFXSCT_SamplerCube:
+         case GFXSCT_SamplerCubeArray:
             glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset));
             glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset));
             break;
             break;
          case GFXSCT_Int2:
          case GFXSCT_Int2:

+ 10 - 2
Engine/source/gfx/gl/gfxGLStateCache.h

@@ -20,11 +20,11 @@ public:
    class TextureUnit
    class TextureUnit
    {
    {
    public:
    public:
-      TextureUnit() :  mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0)
+      TextureUnit() :  mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0), mTextureCubeArray(0)
       {
       {
 
 
       }
       }
-      GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube;
+      GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube, mTextureCubeArray;
    };
    };
 
 
    /// after glBindTexture
    /// after glBindTexture
@@ -45,6 +45,9 @@ public:
       case GL_TEXTURE_CUBE_MAP:
       case GL_TEXTURE_CUBE_MAP:
          mTextureUnits[mActiveTexture].mTextureCube = handle;
          mTextureUnits[mActiveTexture].mTextureCube = handle;
          break;
          break;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         mTextureUnits[mActiveTexture].mTextureCubeArray = handle;
+         break;
       default:
       default:
          AssertFatal(0, avar("GFXGLStateCache::setCacheBindedTex - binding (%x) not supported.", biding) );
          AssertFatal(0, avar("GFXGLStateCache::setCacheBindedTex - binding (%x) not supported.", biding) );
          return;
          return;
@@ -68,6 +71,9 @@ public:
       case GL_TEXTURE_CUBE_MAP:
       case GL_TEXTURE_CUBE_MAP:
          mTextureUnits[mActiveTexture].mTextureCube = handle;
          mTextureUnits[mActiveTexture].mTextureCube = handle;
          break;
          break;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         mTextureUnits[mActiveTexture].mTextureCubeArray = handle;
+         break;
       case GL_FRAMEBUFFER:
       case GL_FRAMEBUFFER:
          mBindedFBO_W = mBindedFBO_R = handle;
          mBindedFBO_W = mBindedFBO_R = handle;
          break;
          break;
@@ -101,6 +107,8 @@ public:
          return mTextureUnits[mActiveTexture].mTexture1D;
          return mTextureUnits[mActiveTexture].mTexture1D;
       case GL_TEXTURE_CUBE_MAP:
       case GL_TEXTURE_CUBE_MAP:
          return mTextureUnits[mActiveTexture].mTextureCube;
          return mTextureUnits[mActiveTexture].mTextureCube;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         return mTextureUnits[mActiveTexture].mTextureCubeArray;
       case GL_DRAW_FRAMEBUFFER:
       case GL_DRAW_FRAMEBUFFER:
          return mBindedFBO_W;
          return mBindedFBO_W;
       case GL_READ_FRAMEBUFFER:
       case GL_READ_FRAMEBUFFER:

+ 3 - 0
Engine/source/gfx/gl/gfxGLUtils.h

@@ -191,6 +191,9 @@ GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_3D, GL_TEXTU
 #define PRESERVE_CUBEMAP_TEXTURE() \
 #define PRESERVE_CUBEMAP_TEXTURE() \
 GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, (GFXGLPreserveInteger::BindFn)glBindTexture)
 GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, (GFXGLPreserveInteger::BindFn)glBindTexture)
 
 
+#define PRESERVE_CUBEMAP_ARRAY_TEXTURE() \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, (GFXGLPreserveInteger::BindFn)glBindTexture)
+
 #define _GET_TEXTURE_BINDING(binding) \
 #define _GET_TEXTURE_BINDING(binding) \
 binding == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : (binding == GL_TEXTURE_3D ?  GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_1D )
 binding == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : (binding == GL_TEXTURE_3D ?  GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_1D )
 
 

+ 32 - 25
Engine/source/renderInstance/renderProbeMgr.cpp

@@ -423,7 +423,7 @@ void RenderProbeMgr::_setupStaticParameters()
       if (!curEntry.mIsEnabled)
       if (!curEntry.mIsEnabled)
          continue;
          continue;
 
 
-      if (curEntry.mIsSkylight)
+      if (curEntry.mProbeShapeType == ProbeRenderInst::ProbeShapeType::Skylight || curEntry.mIsSkylight)
       {
       {
          skylightPos = curEntry.getPosition();
          skylightPos = curEntry.getPosition();
          skylightPrefilterMap = curEntry.mPrefilterCubemap;
          skylightPrefilterMap = curEntry.mPrefilterCubemap;
@@ -680,11 +680,13 @@ void RenderProbeMgr::_update4ProbeConsts(const SceneData &sgData,
          {
          {
             if (curEntry.mPrefilterCubemap.isValid() && curEntry.mPrefilterCubemap.isValid())
             if (curEntry.mPrefilterCubemap.isValid() && curEntry.mPrefilterCubemap.isValid())
             {
             {
-               U32 specSample = probeShaderConsts->mSkylightSpecularMap->getSamplerRegister();
-               U32 irradSample = probeShaderConsts->mSkylightIrradMap->getSamplerRegister();
+               S32 specSample = probeShaderConsts->mSkylightSpecularMap->getSamplerRegister();
+               if (specSample != -1)
+                  GFX->setCubeTexture(specSample, curEntry.mPrefilterCubemap);
 
 
-               GFX->setCubeTexture(probeShaderConsts->mSkylightSpecularMap->getSamplerRegister(), curEntry.mPrefilterCubemap);
-               GFX->setCubeTexture(probeShaderConsts->mSkylightIrradMap->getSamplerRegister(), curEntry.mIrradianceCubemap);
+               S32 irradSample = probeShaderConsts->mSkylightIrradMap->getSamplerRegister();
+               if (irradSample != -1)
+                  GFX->setCubeTexture(irradSample, curEntry.mIrradianceCubemap);
 
 
                shaderConsts->setSafe(probeShaderConsts->mHasSkylight, 1.0f);
                shaderConsts->setSafe(probeShaderConsts->mHasSkylight, 1.0f);
                hasSkylight = true;
                hasSkylight = true;
@@ -718,8 +720,13 @@ void RenderProbeMgr::_update4ProbeConsts(const SceneData &sgData,
       shaderConsts->setSafe(probeShaderConsts->mProbeBoxMaxSC, probeBoxMaxArray);
       shaderConsts->setSafe(probeShaderConsts->mProbeBoxMaxSC, probeBoxMaxArray);
       shaderConsts->setSafe(probeShaderConsts->mProbeConfigDataSC, probeConfigArray);
       shaderConsts->setSafe(probeShaderConsts->mProbeConfigDataSC, probeConfigArray);
 
 
-      GFX->setCubeArrayTexture(probeShaderConsts->mProbeSpecularCubemapSC->getSamplerRegister(), mPrefilterArray);
-      GFX->setCubeArrayTexture(probeShaderConsts->mProbeIrradianceCubemapSC->getSamplerRegister(), mIrradianceArray);
+      S32 specSample = probeShaderConsts->mProbeSpecularCubemapSC->getSamplerRegister();
+      if (specSample != -1)
+         GFX->setCubeArrayTexture(specSample, mPrefilterArray);
+
+      S32 irradSample = probeShaderConsts->mProbeIrradianceCubemapSC->getSamplerRegister();
+      if (irradSample != -1)
+         GFX->setCubeArrayTexture(irradSample, mIrradianceArray);
 
 
       if (!hasSkylight)
       if (!hasSkylight)
          shaderConsts->setSafe(probeShaderConsts->mHasSkylight, 0.0f);
          shaderConsts->setSafe(probeShaderConsts->mHasSkylight, 0.0f);
@@ -779,8 +786,7 @@ void RenderProbeMgr::render( SceneRenderState *state )
    //updateProbes();
    //updateProbes();
 
 
    // Early out if nothing to draw.
    // Early out if nothing to draw.
-   if (!ProbeRenderInst::all.size() || !RenderProbeMgr::smRenderReflectionProbes || !state->isDiffusePass() || (mEffectiveProbeCount == 0
-      || mCubeMapCount != 0 && !hasSkylight))
+   if (!RenderProbeMgr::smRenderReflectionProbes || !state->isDiffusePass() || (!ProbeRenderInst::all.size() || mEffectiveProbeCount == 0 || mCubeMapCount != 0 ) && !hasSkylight)
    {
    {
       getProbeArrayEffect()->setSkip(true);
       getProbeArrayEffect()->setSkip(true);
       return;
       return;
@@ -793,6 +799,19 @@ void RenderProbeMgr::render( SceneRenderState *state )
    // Initialize and set the per-frame parameters after getting
    // Initialize and set the per-frame parameters after getting
    // the vector light material as we use lazy creation.
    // the vector light material as we use lazy creation.
    //_setupPerFrameParameters(state);
    //_setupPerFrameParameters(state);
+
+      //Visualization
+   String useDebugAtten = Con::getVariable("$Probes::showAttenuation", "0");
+   mProbeArrayEffect->setShaderMacro("DEBUGVIZ_ATTENUATION", useDebugAtten);
+
+   String useDebugSpecCubemap = Con::getVariable("$Probes::showSpecularCubemaps", "0");
+   mProbeArrayEffect->setShaderMacro("DEBUGVIZ_SPECCUBEMAP", useDebugSpecCubemap);
+
+   String useDebugDiffuseCubemap = Con::getVariable("$Probes::showDiffuseCubemaps", "0");
+   mProbeArrayEffect->setShaderMacro("DEBUGVIZ_DIFFCUBEMAP", useDebugDiffuseCubemap);
+
+   String useDebugContrib = Con::getVariable("$Probes::showProbeContrib", "0");
+   mProbeArrayEffect->setShaderMacro("DEBUGVIZ_CONTRIB", useDebugContrib);
    
    
    //Array rendering
    //Array rendering
    //U32 probeCount = ProbeRenderInst::all.size();
    //U32 probeCount = ProbeRenderInst::all.size();
@@ -804,23 +823,14 @@ void RenderProbeMgr::render( SceneRenderState *state )
       mProbeArrayEffect->setCubemapTexture(7, skylightIrradMap);
       mProbeArrayEffect->setCubemapTexture(7, skylightIrradMap);
    }
    }
 
 
+   mProbeArrayEffect->setShaderConst("$numProbes", (float)mEffectiveProbeCount);
+
+   mProbeArrayEffect->setShaderConst("$cubeMips", (float)mMipCount);
    if (mEffectiveProbeCount != 0)
    if (mEffectiveProbeCount != 0)
    {
    {
       mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray);
       mProbeArrayEffect->setCubemapArrayTexture(4, mPrefilterArray);
       mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray);
       mProbeArrayEffect->setCubemapArrayTexture(5, mIrradianceArray);
 
 
-      String useDebugAtten = Con::getVariable("$Probes::showAttenuation", "0");
-      mProbeArrayEffect->setShaderMacro("DEBUGVIZ_ATTENUATION", useDebugAtten);
-
-      String useDebugSpecCubemap = Con::getVariable("$Probes::showSpecularCubemaps", "0");
-      mProbeArrayEffect->setShaderMacro("DEBUGVIZ_SPECCUBEMAP", useDebugSpecCubemap);
-
-      String useDebugDiffuseCubemap = Con::getVariable("$Probes::showDiffuseCubemaps", "0");
-      mProbeArrayEffect->setShaderMacro("DEBUGVIZ_DIFFCUBEMAP", useDebugDiffuseCubemap);
-
-      String useDebugContrib = Con::getVariable("$Probes::showProbeContrib", "0");
-      mProbeArrayEffect->setShaderMacro("DEBUGVIZ_CONTRIB", useDebugContrib);
-
       if (useDebugContrib == String("1"))
       if (useDebugContrib == String("1"))
       {
       {
          MRandomLCG RandomGen;
          MRandomLCG RandomGen;
@@ -841,15 +851,12 @@ void RenderProbeMgr::render( SceneRenderState *state )
             else if (i == 2)
             else if (i == 2)
                contribColors[i] = Point4F(0, 0, 1, 1);
                contribColors[i] = Point4F(0, 0, 1, 1);
             else
             else
-               contribColors[i] = Point4F(RandomGen.randF(0, 1), RandomGen.randF(0, 1), RandomGen.randF(0, 1),1);
+               contribColors[i] = Point4F(RandomGen.randF(0, 1), RandomGen.randF(0, 1), RandomGen.randF(0, 1), 1);
          }
          }
 
 
          mProbeArrayEffect->setShaderConst("$probeContribColors", contribColors);
          mProbeArrayEffect->setShaderConst("$probeContribColors", contribColors);
       }
       }
-
-      mProbeArrayEffect->setShaderConst("$cubeMips", (float)mMipCount);
       
       
-      mProbeArrayEffect->setShaderConst("$numProbes", (float)mEffectiveProbeCount);
       mProbeArrayEffect->setShaderConst("$inProbePosArray", probePositionsData);
       mProbeArrayEffect->setShaderConst("$inProbePosArray", probePositionsData);
       mProbeArrayEffect->setShaderConst("$inRefPosArray", probeRefPositionsData);
       mProbeArrayEffect->setShaderConst("$inRefPosArray", probeRefPositionsData);
       mProbeArrayEffect->setShaderConst("$worldToObjArray", probeWorldToObjData);
       mProbeArrayEffect->setShaderConst("$worldToObjArray", probeWorldToObjData);

+ 5 - 0
Templates/Full/game/core/art/grids/materials.cs

@@ -36,6 +36,11 @@ singleton Material( Grid512_Blue_Mat )
    metalness[0] = "0.803922";
    metalness[0] = "0.803922";
    translucent = "1";
    translucent = "1";
    translucentBlendOp = "Add";
    translucentBlendOp = "Add";
+   normalMap[0] = "art/pbr/floor/FloorEbony_normal.png";
+   invertSmoothness[0] = "1";
+   roughMap[0] = "art/pbr/floor/FloorEbony_rough.png";
+   aoMap[0] = "art/pbr/floor/FloorEbony_ao.png";
+   metalMap[0] = "art/pbr/floor/FloorEbony_metal.png";
 };
 };
 
 
 singleton Material( Grid512_ForestGreen_Mat )
 singleton Material( Grid512_ForestGreen_Mat )

+ 6 - 4
Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs

@@ -310,15 +310,17 @@ singleton ShaderData( PFX_ReflectionProbeArray )
    DXVertexShaderFile   = "shaders/common/postFx/postFxV.hlsl";
    DXVertexShaderFile   = "shaders/common/postFx/postFxV.hlsl";
    DXPixelShaderFile    = "shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl";
    DXPixelShaderFile    = "shaders/common/lighting/advanced/reflectionProbeArrayP.hlsl";
 
 
-   //OGLVertexShaderFile  = "shaders/common/postFx/gl//postFxV.glsl";
-   //OGLPixelShaderFile   = "shaders/common/postFx/gl/passthruP.glsl";
+   OGLVertexShaderFile  = "shaders/common/postFx/gl/postFxV.glsl";
+   OGLPixelShaderFile   = "shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl";
    
    
    samplerNames[0] = "$deferredBuffer";
    samplerNames[0] = "$deferredBuffer";
    samplerNames[1] = "$colorBuffer";
    samplerNames[1] = "$colorBuffer";
    samplerNames[2] = "$matInfoBuffer";
    samplerNames[2] = "$matInfoBuffer";
    samplerNames[3] = "$BRDFTexture";
    samplerNames[3] = "$BRDFTexture";
-   samplerNames[4] = "$cubeMap";
-   samplerNames[5] = "$irradianceCubemap";
+   samplerNames[4] = "$specularCubemapAR";
+   samplerNames[5] = "$irradianceCubemapAR";
+   samplerNames[6] = "$skylightSpecularMap";
+   samplerNames[7] = "$skylightIrradMap";
 
 
    pixVersion = 2.0;
    pixVersion = 2.0;
 };  
 };  

+ 34 - 7
Templates/Full/game/levels/Pbr Mat Test.mis

@@ -99,18 +99,17 @@ new SimGroup(MissionGroup) {
    };
    };
    new Skylight(theSkyLight) {
    new Skylight(theSkyLight) {
       enabled = "1";
       enabled = "1";
-      ProbeShape = "Box";
-      radius = "10";
-      posOffset = "0 0 0";
       ReflectionMode = "Baked Cubemap";
       ReflectionMode = "Baked Cubemap";
-      reflectionPath = "levels/Timmy Test/probes/";
-      Bake = "0";
-      position = "8.74661 10.1457 2.48852";
+      position = "8.74661 10.1457 2.94337";
       rotation = "1 0 0 0";
       rotation = "1 0 0 0";
-      scale = "1 1 1";
       canSave = "1";
       canSave = "1";
       canSaveDynamicFields = "1";
       canSaveDynamicFields = "1";
       persistentId = "247d7009-db8a-11e8-87b8-ed691a78e155";
       persistentId = "247d7009-db8a-11e8-87b8-ed691a78e155";
+         posOffset = "0 0 0";
+         ProbeShape = "Box";
+         radius = "10";
+         reflectionPath = "levels/Timmy Test/probes/";
+         scale = "1 1 1";
    };
    };
    new TSStatic() {
    new TSStatic() {
       shapeName = "art/shapes/material_ball/material_ball.dae";
       shapeName = "art/shapes/material_ball/material_ball.dae";
@@ -351,5 +350,33 @@ new SimGroup(MissionGroup) {
       canSave = "1";
       canSave = "1";
       canSaveDynamicFields = "1";
       canSaveDynamicFields = "1";
    };
    };
+   new ConvexShape() {
+      Material = "Grid512_Blue_Mat";
+      position = "-6.41329 13.7734 2.12278";
+      rotation = "0 0 1 3.19212";
+      scale = "1 1 1";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+
+      surface = "0 0 0 1 0 0 0.384079";
+      surface = "0 1 0 0 0 0 -0.384079";
+      surface = "0.707107 0 0 0.707107 0 0.285194 0";
+      surface = "0 0.707107 -0.707107 0 0 -0.285194 -1.42109e-14";
+      surface = "0.5 0.5 -0.5 0.5 -0.758333 0 -1.07696e-07";
+      surface = "0.5 -0.5 0.5 0.5 0.758333 0 -1.07696e-07";
+   };
+   new BoxEnvironmentProbe() {
+      enabled = "0";
+      refOffset = "0 0 0";
+      refScale = "10 10 10";
+      ReflectionMode = "Baked Cubemap";
+      position = "-11.7544 15.2634 4.67576";
+      rotation = "1 0 0 0";
+      scale = "10 10 10";
+      canSave = "1";
+      canSaveDynamicFields = "1";
+      persistentId = "8c7e1f23-5f1c-11e9-8089-c88cdaba85a3";
+      attenuation = "1";
+   };
 };
 };
 //--- OBJECT WRITE END ---
 //--- OBJECT WRITE END ---

+ 163 - 6
Templates/Full/game/shaders/common/gl/lighting.glsl

@@ -44,6 +44,9 @@ uniform vec4 albedo;
 
 
 #endif // !TORQUE_SHADERGEN
 #endif // !TORQUE_SHADERGEN
 
 
+#define MAX_PROBES 50
+#define MAX_FORWARD_PROBES 4
+
 vec3 getDistanceVectorToPlane( vec3 origin, vec3 direction, vec4 plane )
 vec3 getDistanceVectorToPlane( vec3 origin, vec3 direction, vec4 plane )
 {
 {
    float denum = dot( plane.xyz, direction.xyz );
    float denum = dot( plane.xyz, direction.xyz );
@@ -119,15 +122,15 @@ void Surface::Update()
 	F = F_Schlick(f0, f90, NdotV);
 	F = F_Schlick(f0, f90, NdotV);
 }
 }
 	
 	
-Surface createSurface(vec4 gbuffer0, sampler2D gbufferTex1, sampler2D gbufferTex2, in vec2 uv, in vec3 wsEyePos, in vec3 wsEyeRay, in mat4 invView)
+Surface createSurface(vec4 normDepth, sampler2D colorBuffer, sampler2D matInfoBuffer, in vec2 uv, in vec3 wsEyePos, in vec3 wsEyeRay, in mat4 invView)
 {
 {
 	Surface surface;// = Surface();
 	Surface surface;// = Surface();
 
 
-	vec4 gbuffer1 = texture(gbufferTex1, uv);
-	vec4 gbuffer2 = texture(gbufferTex2, uv);
-	surface.depth = gbuffer0.a;
+	vec4 gbuffer1 = texture(colorBuffer, uv);
+	vec4 gbuffer2 = texture(matInfoBuffer, uv);
+	surface.depth = normDepth.a;
 	surface.P = wsEyePos + wsEyeRay * surface.depth;
 	surface.P = wsEyePos + wsEyeRay * surface.depth;
-	surface.N = tMul(invView, vec4(gbuffer0.xyz,0)).xyz; //TODO move t3d to use WS normals
+	surface.N = tMul(invView, vec4(normDepth.xyz,0)).xyz; //TODO move t3d to use WS normals
 	surface.V = normalize(wsEyePos - surface.P);
 	surface.V = normalize(wsEyePos - surface.P);
 	surface.baseColor = gbuffer1;
 	surface.baseColor = gbuffer1;
 	const float minRoughness=1e-4;
 	const float minRoughness=1e-4;
@@ -266,4 +269,158 @@ vec3 directSpecular(vec3 N, vec3 V, vec3 L, float roughness, float F0)
 
 
 	float specular = dotNL * D * F * vis;
 	float specular = dotNL * D * F * vis;
 	return vec3(specular,specular,specular);
 	return vec3(specular,specular,specular);
-}
+}
+
+//Probe IBL stuff
+float defineSphereSpaceInfluence(vec3 wsPosition, vec3 wsProbePosition, float radius)
+{
+   vec3 L = wsProbePosition.xyz - wsPosition;
+   float contribution = 1.0 - length(L) / radius;
+   return contribution;
+}
+
+float getDistBoxToPoint(vec3 pt, vec3 extents)
+{
+   vec3 d = max(max(-extents - pt, 0), pt - extents);
+   return max(max(d.x, d.y), d.z);
+}
+
+float defineBoxSpaceInfluence(vec3 wsPosition, mat4 worldToObj, float attenuation)
+{
+   vec3 surfPosLS = tMul(worldToObj, vec4(wsPosition, 1.0)).xyz;
+   float atten = 1.0 - attenuation;
+   float baseVal = 0.25;
+   float dist = getDistBoxToPoint(surfPosLS, vec3(baseVal, baseVal, baseVal));
+   return saturate(smoothstep(baseVal + 0.0001, atten*baseVal, dist));
+}
+
+// Box Projected IBL Lighting
+// Based on: http://www.gamedev.net/topic/568829-box-projected-cubemap-environment-mapping/
+// and https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
+vec3 boxProject(vec3 wsPosition, vec3 wsReflectVec, mat4 worldToObj, vec3 bbMin, vec3 bbMax, vec3 refPosition)
+{
+   vec3 RayLS = tMul(worldToObj, vec4(wsReflectVec, 0.0)).xyz;
+   vec3 PositionLS = tMul(worldToObj, vec4(wsPosition, 1.0)).xyz;
+
+   vec3 unit = bbMax.xyz - bbMin.xyz;
+   vec3 plane1vec = (unit / 2 - PositionLS) / RayLS;
+   vec3 plane2vec = (-unit / 2 - PositionLS) / RayLS;
+   vec3 furthestPlane = max(plane1vec, plane2vec);
+   float dist = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z);
+   vec3 posonbox = wsPosition + wsReflectVec * dist;
+
+   return posonbox - refPosition.xyz;
+}
+
+/*vec4 computeForwardProbes(Surface surface,
+    float cubeMips, float numProbes, mat4 worldToObjArray[MAX_FORWARD_PROBES], vec4 probeConfigData[MAX_FORWARD_PROBES], 
+    vec4 inProbePosArray[MAX_FORWARD_PROBES], vec4 bbMinArray[MAX_FORWARD_PROBES], vec4 bbMaxArray[MAX_FORWARD_PROBES], vec4 inRefPosArray[MAX_FORWARD_PROBES],
+    float hasSkylight, samplerCube skylightIrradMap, samplerCube skylightSpecularMap,
+    sampler2D BRDFTexture, samplerCubeArray irradianceCubemapAR,
+    samplerCubeArray specularCubemapAR)
+{
+	return vec4(0,0,0,1);
+
+  int i = 0;
+   float blendFactor[MAX_FORWARD_PROBES];
+   float blendSum = 0;
+   float blendFacSum = 0;
+   float invBlendSum = 0;
+   float probehits = 0;
+   //Set up our struct data
+   float contribution[MAX_FORWARD_PROBES];
+  for (i = 0; i < numProbes; ++i)
+  {
+      contribution[i] = 0;
+
+      if (probeConfigData[i].r == 0) //box
+      {
+         contribution[i] = defineBoxSpaceInfluence(surface.P, worldToObjArray[i], probeConfigData[i].b);
+         if (contribution[i] > 0.0)
+            probehits++;
+      }
+      else if (probeConfigData[i].r == 1) //sphere
+      {
+         contribution[i] = defineSphereSpaceInfluence(surface.P, inProbePosArray[i].xyz, probeConfigData[i].g);
+         if (contribution[i] > 0.0)
+            probehits++;
+      }
+
+      contribution[i] = max(contribution[i], 0);
+
+      blendSum += contribution[i];
+      invBlendSum += (1.0f - contribution[i]);
+   }
+
+   if (probehits > 1.0)
+   {
+      for (i = 0; i < numProbes; i++)
+      {
+         blendFactor[i] = ((contribution[i] / blendSum)) / probehits;
+         blendFactor[i] *= ((contribution[i]) / invBlendSum);
+         blendFactor[i] = saturate(blendFactor[i]);
+         blendFacSum += blendFactor[i];
+      }
+
+      // Normalize blendVal
+      if (blendFacSum == 0.0f) // Possible with custom weight
+      {
+         blendFacSum = 1.0f;
+      }
+
+      float invBlendSumWeighted = 1.0f / blendFacSum;
+      for (i = 0; i < numProbes; ++i)
+      {
+         blendFactor[i] *= invBlendSumWeighted;
+         contribution[i] *= blendFactor[i];
+         //alpha -= contribution[i];
+      }
+   }
+   //else
+   //   alpha -= blendSum;
+
+   vec3 irradiance = vec3(0, 0, 0);
+   vec3 specular = vec3(0, 0, 0);
+
+   // Radiance (Specular)
+   float lod = surface.roughness*cubeMips;
+
+   float alpha = 1;
+   for (i = 0; i < numProbes; ++i)
+   {
+      float contrib = contribution[i];
+      if (contrib != 0)
+      {
+         int cubemapIdx = probeConfigData[i].a;
+         vec3 dir = boxProject(surface.P, surface.R, worldToObjArray[i], bbMinArray[i].xyz, bbMaxArray[i].xyz, inRefPosArray[i].xyz);
+
+         irradiance += textureLod(irradianceCubemapAR, vec4(dir, cubemapIdx), 0).xyz * contrib;
+         specular += textureLod(specularCubemapAR, vec4(dir, cubemapIdx), lod).xyz * contrib;
+         alpha -= contrib;
+      }
+   }
+
+   if (hasSkylight == 1 && alpha > 0.001)
+   {
+      irradiance += textureLod(skylightIrradMap, surface.R, 0).xyz * alpha;
+      specular += textureLod(skylightSpecularMap, surface.R, lod).xyz * alpha;
+   }
+
+   vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness);
+
+   //energy conservation
+   vec3 kD = 1.0.xxx - F;
+   kD *= 1.0 - surface.metalness;
+
+   //apply brdf
+   //Do it once to save on texture samples
+   vec2 brdf = texture(BRDFTexture, vec2(surface.roughness, surface.NdotV)).xy;
+   specular *= brdf.x * F + brdf.y;
+
+   //final diffuse color
+   vec3 diffuse = kD * irradiance * surface.baseColor.rgb;
+   vec4 finalColor = vec4(diffuse + specular * surface.ao, 1.0);
+
+   finalColor = vec4(irradiance.rgb,1);
+   return finalColor;
+}*/

+ 208 - 59
Templates/Full/game/shaders/common/lighting/advanced/gl/reflectionProbeArrayP.glsl

@@ -1,63 +1,212 @@
-//-----------------------------------------------------------------------------
-// Copyright (c) 2012 GarageGames, LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to
-// deal in the Software without restriction, including without limitation the
-// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-// sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-// IN THE SOFTWARE.
-//-----------------------------------------------------------------------------
-
-#include "../../torque.hlsl"
-
-struct ConnectData
-{
-    float4 hpos    : TORQUE_POSITION;
-    float2 uv      : TEXCOORD;
-};
+#include "../../../gl/hlslCompat.glsl"
+#include "../../../postFx/gl/postFx.glsl"
+#include "../../../gl/torque.glsl"
+#include "shadergen:/autogenConditioners.h"
+#include "../../../gl/lighting.glsl"
+
+#line 7
+
+uniform sampler2D deferredBuffer;
+uniform sampler2D colorBuffer;
+uniform sampler2D matInfoBuffer;
+uniform sampler2D BRDFTexture;
+
+uniform vec4 rtParams0;
+uniform vec4 vsFarPlane;
+uniform mat4 cameraToWorld;
+uniform vec3 eyePosWorld;
+
+//cubemap arrays require all the same size. so shared mips# value
+uniform float cubeMips;
+
+uniform float numProbes;
+uniform samplerCubeArray specularCubemapAR;
+uniform samplerCubeArray irradianceCubemapAR;
+
+uniform vec4    inProbePosArray[MAX_PROBES];
+uniform vec4    inRefPosArray[MAX_PROBES];
+uniform mat4    worldToObjArray[MAX_PROBES];
+uniform vec4    bbMinArray[MAX_PROBES];
+uniform vec4    bbMaxArray[MAX_PROBES];
+uniform vec4    probeConfigData[MAX_PROBES];   //r,g,b/mode,radius,atten
+
+#if DEBUGVIZ_CONTRIB
+uniform vec4    probeContribColors[MAX_PROBES];
+#endif
 
 
-uniform int face;
+uniform samplerCube skylightSpecularMap;
+uniform samplerCube skylightIrradMap;
+uniform float hasSkylight;
 
 
-TORQUE_UNIFORM_SAMPLERCUBE(environmentMap, 0);
+out vec4 OUT_col;
 
 
-float4 main(ConnectData IN) : TORQUE_TARGET0
+void main()
 {
 {
-    float3 N = getCubeDir(face,IN.uv);
-    float3 irradiance = 0;
-    
-    // tangent space calculation from origin point
-    float3 up    = float3(0.0, 0.0, 1.0);
-    float3 right = cross(up, N);
-    up           = cross(N, right);
-       
-    float sampleDelta = 0.025;
-    int nrSamples = 0;
-    for(float phi = 0.0; phi < M_2PI_F; phi += sampleDelta)
-    {
-        for(float theta = 0.0; theta < M_HALFPI_F; theta += sampleDelta)
-        {
-            // spherical to cartesian (in tangent space)
-            float3 tangentSample = float3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));
-            // tangent space to world
-            float3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 
-
-            irradiance += TORQUE_TEXCUBE(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
-            nrSamples++;
-        }
-    }
-    irradiance = M_PI_F * irradiance * (1.0 / float(nrSamples));
-    
-    return float4(irradiance, 1.0);
-}
+   //unpack normal and linear depth 
+   vec4 normDepth = deferredUncondition(deferredBuffer, IN_uv0.xy);
+
+   //create surface
+   Surface surface = createSurface(normDepth, colorBuffer, matInfoBuffer, IN_uv0.xy, eyePosWorld, IN_wsEyeRay, cameraToWorld);
+
+   //early out if emissive
+   if (getFlag(surface.matFlag, 0))
+   {
+      discard;
+   }
+
+   float alpha = 1;
+
+   int i = 0;
+   float blendFactor[MAX_PROBES];
+   float blendSum = 0;
+   float blendFacSum = 0;
+   float invBlendSum = 0;
+   float probehits = 0;
+   //Set up our struct data
+   float contribution[MAX_PROBES];
+   if (alpha > 0)
+   {
+      //Process prooooobes
+      for (i = 0; i < numProbes; ++i)
+      {
+         contribution[i] = 0;
+
+         if (probeConfigData[i].r == 0) //box
+         {
+            contribution[i] = defineBoxSpaceInfluence(surface.P, worldToObjArray[i], probeConfigData[i].b);
+            if (contribution[i]>0.0)
+               probehits++;
+         }
+         else if (probeConfigData[i].r == 1) //sphere
+         {
+            contribution[i] = defineSphereSpaceInfluence(surface.P, inProbePosArray[i].xyz, probeConfigData[i].g);
+            if (contribution[i]>0.0)
+               probehits++;
+         }
+
+         contribution[i] = max(contribution[i],0);
+
+         blendSum += contribution[i];
+         invBlendSum += (1.0f - contribution[i]);
+      }
+      // Weight0 = normalized NDF, inverted to have 1 at center, 0 at boundary.
+	   // And as we invert, we need to divide by Num-1 to stay normalized (else sum is > 1). 
+	   // respect constraint B.
+	   // Weight1 = normalized inverted NDF, so we have 1 at center, 0 at boundary
+	   // and respect constraint A.
+      
+      if (probehits>1.0)
+	{
+            for (i = 0; i < numProbes; i++)
+            {
+                  blendFactor[i] = ((contribution[i] / blendSum)) / probehits;
+                  blendFactor[i] *= ((contribution[i]) / invBlendSum);
+                  blendFactor[i] = saturate(blendFactor[i]);
+                  blendFacSum += blendFactor[i];
+            }
+
+      // Normalize blendVal
+#if DEBUGVIZ_ATTENUATION == 0 //this can likely be removed when we fix the above normalization behavior
+            if (blendFacSum == 0.0f) // Possible with custom weight
+            {
+                  blendFacSum = 1.0f;
+            }
+#endif
+
+            float invBlendSumWeighted = 1.0f / blendFacSum;
+            for (i = 0; i < numProbes; ++i)
+            {
+                  blendFactor[i] *= invBlendSumWeighted;
+                  contribution[i] *= blendFactor[i];
+                  alpha -= contribution[i];
+            }
+      }
+      else
+            alpha -= blendSum;
+      
+#if DEBUGVIZ_ATTENUATION == 1
+      float contribAlpha = 1;
+      for (i = 0; i < numProbes; ++i)
+      {
+         contribAlpha -= contribution[i];
+      }
+
+      OUT_col = vec4(1 - contribAlpha, 1 - contribAlpha, 1 - contribAlpha, 1);
+      return;
+#endif
+
+#if DEBUGVIZ_CONTRIB == 1
+      vec3 finalContribColor = vec3(0, 0, 0);
+      float contribAlpha = 1;
+      for (i = 0; i < numProbes; ++i)
+      {
+         finalContribColor += contribution[i] *probeContribColors[i].rgb;
+         contribAlpha -= contribution[i];
+      }
+
+      //Skylight coloration for anything not covered by probes above
+      finalContribColor += vec3(0.3, 0.3, 0.3) * contribAlpha;
+
+      OUT_col = vec4(finalContribColor, 1);
+      return;
+#endif
+   }
+
+   vec3 irradiance = vec3(0, 0, 0);
+   vec3 specular = vec3(0, 0, 0);
+
+   // Radiance (Specular)
+#if DEBUGVIZ_SPECCUBEMAP == 0
+   float lod = surface.roughness*cubeMips;
+#elif DEBUGVIZ_SPECCUBEMAP == 1
+   float lod = 0;
+#endif
+
+   alpha = 1;
+   for (i = 0; i < numProbes; ++i)
+   {
+      float contrib = contribution[i];
+      if (contrib != 0)
+      {
+         float cubemapIdx = probeConfigData[i].a;
+         vec3 dir = boxProject(surface.P, surface.R, worldToObjArray[i], bbMinArray[i].xyz, bbMaxArray[i].xyz, inRefPosArray[i].xyz);
+
+         //irradiance += textureLod(irradianceCubemapAR, vec4(dir, cubemapIdx), 0).xyz * contrib;
+         //specular += textureLod(specularCubemapAR, vec4(dir, cubemapIdx), lod).xyz * contrib;
+         irradiance += vec3(1,1,1) * contrib;
+         specular += vec3(1,1,1) * contrib;
+         alpha -= contrib;
+      }
+   }
+
+   if (hasSkylight == 1 && alpha > 0.001)
+   {
+      irradiance += textureLod(skylightIrradMap, surface.R, 0).xyz * alpha;
+      specular += textureLod(skylightSpecularMap, surface.R, lod).xyz * alpha;
+   }
+
+#if DEBUGVIZ_SPECCUBEMAP == 1 && DEBUGVIZ_DIFFCUBEMAP == 0
+   OUT_col = vec4(specular, 1);
+   return;
+#elif DEBUGVIZ_DIFFCUBEMAP == 1
+   OUT_col = vec4(irradiance, 1);
+   return;
+#endif
+
+   vec3 F = FresnelSchlickRoughness(surface.NdotV, surface.f0, surface.roughness);
+
+   //energy conservation
+   vec3 kD = vec3(1,1,1) - F;
+   kD *= 1.0 - surface.metalness;
+
+   //apply brdf
+   //Do it once to save on texture samples
+   vec2 brdf = texture(BRDFTexture, vec2(surface.roughness, surface.NdotV)).xy;
+   specular *= brdf.x * F + brdf.y;
+
+   //final diffuse color
+   vec3 diffuse = kD * irradiance * surface.baseColor.rgb;
+   vec4 finalColor = vec4(diffuse + specular * surface.ao, 1.0);
+
+   OUT_col = finalColor;
+}

+ 1 - 0
gl/Timmy Note.txt

@@ -0,0 +1 @@
+idiot me used glTexImage2D for the cubemap array code, obviously should be glTexImage3D

+ 389 - 0
gl/gfxGLCubemap.cpp

@@ -0,0 +1,389 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "gfx/gl/gfxGLDevice.h"
+#include "gfx/gl/gfxGLTextureObject.h"
+#include "gfx/gl/gfxGLEnumTranslate.h"
+#include "gfx/gl/gfxGLUtils.h"
+#include "gfx/gl/gfxGLCubemap.h"
+#include "gfx/gfxTextureManager.h"
+#include "gfx/gfxCardProfile.h"
+#include "gfx/bitmap/ddsFile.h"
+#include "gfx/bitmap/imageUtils.h"
+
+
+static GLenum faceList[6] = 
+{ 
+   GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+   GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+   GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+};
+
+GFXGLCubemap::GFXGLCubemap() :
+      mCubemap(0), 
+      mDynamicTexSize(0),
+      mFaceFormat( GFXFormatR8G8B8A8 )
+{
+   for(U32 i = 0; i < 6; i++)
+      mTextures[i] = NULL;
+   
+   GFXTextureManager::addEventDelegate( this, &GFXGLCubemap::_onTextureEvent );
+}
+
+GFXGLCubemap::~GFXGLCubemap()
+{
+   glDeleteTextures(1, &mCubemap);
+   GFXTextureManager::removeEventDelegate( this, &GFXGLCubemap::_onTextureEvent );
+}
+
+GLenum GFXGLCubemap::getEnumForFaceNumber(U32 face)
+{ 
+   return faceList[face];
+}
+
+void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces)
+{
+   AssertFatal( faces, "");
+   AssertFatal( faces[0]->mMipLevels > 0, "");
+
+   PRESERVE_CUBEMAP_TEXTURE();
+   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, faces[0]->mMipLevels - 1 );
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+   
+   U32 reqWidth = faces[0]->getWidth();
+   U32 reqHeight = faces[0]->getHeight();
+   GFXFormat regFaceFormat = faces[0]->getFormat();
+   const bool isCompressed = ImageUtil::isCompressedFormat(regFaceFormat);
+   mWidth = reqWidth;
+   mHeight = reqHeight;
+   mFaceFormat = regFaceFormat;
+   mMipMapLevels = getMax( (U32)1, faces[0]->mMipLevels);
+   AssertFatal(reqWidth == reqHeight, "GFXGLCubemap::fillCubeTextures - Width and height must be equal!");
+   
+   for(U32 i = 0; i < 6; i++)
+   {
+      AssertFatal(faces[i], avar("GFXGLCubemap::fillCubeFaces - texture %i is NULL!", i));
+      AssertFatal((faces[i]->getWidth() == reqWidth) && (faces[i]->getHeight() == reqHeight), "GFXGLCubemap::fillCubeFaces - All textures must have identical dimensions!");
+      AssertFatal(faces[i]->getFormat() == regFaceFormat, "GFXGLCubemap::fillCubeFaces - All textures must have identical formats!");
+      
+      mTextures[i] = faces[i];
+      GFXFormat faceFormat = faces[i]->getFormat();
+
+        GFXGLTextureObject* glTex = static_cast<GFXGLTextureObject*>(faces[i].getPointer());
+        if( isCompressed )
+        {
+            for( U32 mip = 0; mip < mMipMapLevels; ++mip )
+            {
+                const U32 mipWidth  = getMax( U32(1), faces[i]->getWidth() >> mip );
+                const U32 mipHeight = getMax( U32(1), faces[i]->getHeight() >> mip );
+                const U32 mipDataSize = getCompressedSurfaceSize( mFaceFormat, mWidth, mHeight, mip );
+
+                U8* buf = glTex->getTextureData( mip );
+                glCompressedTexImage2D(faceList[i], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0, mipDataSize, buf);
+                delete[] buf;
+            }
+        }
+        else
+        {
+            U8* buf = glTex->getTextureData();
+            glTexImage2D(faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], mWidth, mHeight, 
+                0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], buf);
+            delete[] buf;
+        }
+   }
+   
+    if( !isCompressed )
+        glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+}
+
+void GFXGLCubemap::initStatic(GFXTexHandle* faces)
+{
+   if(mCubemap)
+      return;
+      
+   if(faces)
+   {
+      AssertFatal(faces[0], "GFXGLCubemap::initStatic - empty texture passed");
+      glGenTextures(1, &mCubemap);
+      fillCubeTextures(faces);
+   }
+}
+
+void GFXGLCubemap::initStatic( DDSFile *dds )
+{
+   if(mCubemap)
+      return;
+      
+   AssertFatal( dds, "GFXGLCubemap::initStatic - Got null DDS file!" );
+   AssertFatal( dds->isCubemap(), "GFXGLCubemap::initStatic - Got non-cubemap DDS file!" );
+   AssertFatal( dds->mSurfaces.size() == 6, "GFXGLCubemap::initStatic - DDS has less than 6 surfaces!" );
+
+   mWidth = dds->getWidth();
+   mHeight = dds->getHeight();
+   mFaceFormat = dds->getFormat();
+   mMipMapLevels = dds->getMipLevels();
+   const bool isCompressed = ImageUtil::isCompressedFormat(mFaceFormat);
+   glGenTextures(1, &mCubemap);
+
+   PRESERVE_CUBEMAP_TEXTURE();
+   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+   AssertFatal(mWidth == mHeight, "GFXGLCubemap::initStatic - Width and height must be equal!");
+   
+   for(U32 i = 0; i < 6; i++)
+   {
+      if ( !dds->mSurfaces[i] )
+      {
+         // TODO: The DDS can skip surfaces, but i'm unsure what i should
+         // do here when creating the cubemap.  Ignore it for now.
+         continue;
+      }
+
+      // convert to Z up
+      const U32 faceIndex = _zUpFaceIndex(i);
+
+      // Now loop thru the mip levels!
+      for (U32 mip = 0; mip < mMipMapLevels; ++mip)
+      {
+         const U32 mipWidth  = getMax( U32(1), mWidth >> mip );
+         const U32 mipHeight = getMax( U32(1), mHeight >> mip );
+         if (isCompressed)
+            glCompressedTexImage2D(faceList[faceIndex], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0, dds->getSurfaceSize(mip), dds->mSurfaces[i]->mMips[mip]);
+         else
+            glTexImage2D(faceList[faceIndex], mip, GFXGLTextureInternalFormat[mFaceFormat], mipWidth, mipHeight, 0,
+               GFXGLTextureFormat[mFaceFormat], GFXGLTextureType[mFaceFormat], dds->mSurfaces[i]->mMips[mip]);
+      }
+   }
+}
+
+void GFXGLCubemap::initDynamic(U32 texSize, GFXFormat faceFormat, U32 mipLevels)
+{
+   mDynamicTexSize = texSize;
+   mFaceFormat = faceFormat;
+   const bool isCompressed = ImageUtil::isCompressedFormat(faceFormat);
+   mMipMapLevels = getMax( (U32)1, getMaxMipmaps( texSize, texSize, 1 ) );
+
+   glGenTextures(1, &mCubemap);
+   PRESERVE_CUBEMAP_TEXTURE();
+   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+   mWidth = texSize;
+   mHeight = texSize;
+
+    for(U32 i = 0; i < 6; i++)
+    {
+        if( ImageUtil::isCompressedFormat(faceFormat) )
+        {
+            for( U32 mip = 0; mip < mMipMapLevels; ++mip )
+            {
+                const U32 mipSize = getMax( U32(1), texSize >> mip );
+                const U32 mipDataSize = getCompressedSurfaceSize( mFaceFormat, texSize, texSize, mip );
+                glCompressedTexImage2D(faceList[i], mip, GFXGLTextureInternalFormat[mFaceFormat], mipSize, mipSize, 0, mipDataSize, NULL);
+            }
+        }
+        else
+        {
+            glTexImage2D( faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], texSize, texSize, 
+                0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], NULL);
+        }
+    }
+
+    if( !isCompressed )
+        glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+}
+
+void GFXGLCubemap::zombify()
+{
+   glDeleteTextures(1, &mCubemap);
+   mCubemap = 0;
+}
+
+void GFXGLCubemap::resurrect()
+{
+   // Handled in tmResurrect
+}
+
+void GFXGLCubemap::tmResurrect()
+{
+   if(mDynamicTexSize)
+      initDynamic(mDynamicTexSize,mFaceFormat);
+   else
+   {
+      if ( mDDSFile )
+         initStatic( mDDSFile );
+      else
+         initStatic( mTextures );
+   }
+}
+
+void GFXGLCubemap::setToTexUnit(U32 tuNum)
+{
+   static_cast<GFXGLDevice*>(getOwningDevice())->setCubemapInternal(tuNum, this);
+}
+
+void GFXGLCubemap::bind(U32 textureUnit) const
+{
+   glActiveTexture(GL_TEXTURE0 + textureUnit);
+   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
+   static_cast<GFXGLDevice*>(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_CUBE_MAP, mCubemap);
+   
+   GFXGLStateBlockRef sb = static_cast<GFXGLDevice*>(GFX)->getCurrentStateBlock();
+   AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!");
+   if (!sb)
+      return;   
+      
+   const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit];
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0));   
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]);   
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]);
+}
+
+void GFXGLCubemap::_onTextureEvent( GFXTexCallbackCode code )
+{
+   if ( code == GFXZombify )
+      zombify();
+   else
+      tmResurrect();
+}
+
+U8* GFXGLCubemap::getTextureData(U32 face, U32 mip)
+{
+   AssertFatal(mMipMapLevels, "");
+   mip = (mip < mMipMapLevels) ? mip : 0;
+   const U32 bytesPerTexel = 4; //TODO make work with more formats!!!!!
+   const U32 dataSize = ImageUtil::isCompressedFormat(mFaceFormat)
+      ? getCompressedSurfaceSize(mFaceFormat, mWidth, mHeight, mip)
+      : (mWidth >> mip) * (mHeight >> mip) * bytesPerTexel;
+
+   U8* data = new U8[dataSize];
+   PRESERVE_TEXTURE(GL_TEXTURE_CUBE_MAP);
+   glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap);
+
+   if (ImageUtil::isCompressedFormat(mFaceFormat))
+      glGetCompressedTexImage(faceList[face], mip, data);
+   else
+      glGetTexImage(faceList[face], mip, GFXGLTextureFormat[mFaceFormat], GFXGLTextureType[mFaceFormat], data);
+
+   return data;
+}
+
+//-----------------------------------------------------------------------------
+// Cubemap Array
+//-----------------------------------------------------------------------------
+
+GFXGLCubemapArray::GFXGLCubemapArray()
+{
+}
+
+GFXGLCubemapArray::~GFXGLCubemapArray()
+{
+   glDeleteTextures(1, &mCubemap);
+}
+
+void GFXGLCubemapArray::initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount)
+{
+   AssertFatal(cubemaps, "GFXGLCubemapArray- Got null GFXCubemapHandle!");
+   AssertFatal(*cubemaps, "GFXGLCubemapArray - Got empty cubemap!");
+
+   //all cubemaps must be the same size,format and number of mipmaps. Grab the details from the first cubemap
+   mSize = cubemaps[0]->getSize();
+   mFormat = cubemaps[0]->getFormat();
+   mMipMapLevels = cubemaps[0]->getMipMapLevels();
+   mNumCubemaps = cubemapCount;
+   const bool isCompressed = ImageUtil::isCompressedFormat(mFormat);
+
+   glGenTextures(1, &mCubemap);
+   PRESERVE_CUBEMAP_ARRAY_TEXTURE();
+   glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAX_LEVEL, mMipMapLevels - 1);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+   for (U32 i = 0; i < cubemapCount; i++)
+   {
+      GFXGLCubemap* glTex = static_cast<GFXGLCubemap*>(cubemaps[i].getPointer());
+      for (U32 face = 0; face < 6; face++)
+      {
+         for (U32 currentMip = 0; currentMip < mMipMapLevels; currentMip++)
+         {
+            U8 *pixelData = glTex->getTextureData(face, currentMip);
+            const U32 mipSize = getMax(U32(1), mSize >> currentMip);
+            if (isCompressed)
+            {               
+               const U32 mipDataSize = getCompressedSurfaceSize(mFormat, mSize, mSize, currentMip);
+               glCompressedTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize, 0, mipDataSize, pixelData);
+            }
+            else
+            {               
+               glTexImage2D(faceList[face], currentMip, GFXGLTextureInternalFormat[mFormat], mipSize, mipSize,
+                  0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], pixelData);
+            }
+            delete[] pixelData;
+         }
+      }
+   }
+}
+
+void GFXGLCubemapArray::setToTexUnit(U32 tuNum)
+{
+   static_cast<GFXGLDevice*>(getOwningDevice())->setCubemapArrayInternal(tuNum, this);
+}
+
+void GFXGLCubemapArray::bind(U32 textureUnit) const
+{
+   glActiveTexture(GL_TEXTURE0 + textureUnit);
+   glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+   static_cast<GFXGLDevice*>(getOwningDevice())->getOpenglCache()->setCacheBindedTex(textureUnit, GL_TEXTURE_CUBE_MAP_ARRAY, mCubemap);
+
+   GFXGLStateBlockRef sb = static_cast<GFXGLDevice*>(GFX)->getCurrentStateBlock();
+   AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!");
+   if (!sb)
+      return;
+
+   const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit];
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0));
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]);
+   glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]);
+}

+ 113 - 0
gl/gfxGLCubemap.h

@@ -0,0 +1,113 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _GFXGLCUBEMAP_H_
+#define _GFXGLCUBEMAP_H_
+
+#ifndef _GFXCUBEMAP_H_
+#include "gfx/gfxCubemap.h"
+#endif
+#ifndef __RESOURCE_H__
+#include "core/resource.h"
+#endif
+
+
+class GFXGLCubemap : public GFXCubemap
+{
+public:
+   GFXGLCubemap();
+   virtual ~GFXGLCubemap();
+
+   virtual void initStatic( GFXTexHandle *faces );
+   virtual void initStatic( DDSFile *dds );
+   virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8, U32 mipLevels = 0);
+   virtual U32 getSize() const { return mWidth; }
+   virtual GFXFormat getFormat() const { return mFaceFormat; }
+
+   // Convenience methods for GFXGLTextureTarget
+   U32 getWidth() { return mWidth; }
+   U32 getHeight() { return mHeight; }
+   U32 getHandle() { return mCubemap; }
+   
+   // GFXResource interface
+   virtual void zombify();
+   virtual void resurrect();
+   
+   /// Called by texCB; this is to ensure that all textures have been resurrected before we attempt to res the cubemap.
+   void tmResurrect();
+   
+   static GLenum getEnumForFaceNumber(U32 face);///< Performs lookup to get a GLenum for the given face number
+
+   /// @return An array containing the texture data
+   /// @note You are responsible for deleting the returned data! (Use delete[])
+   U8* getTextureData(U32 face, U32 mip = 0);
+
+protected:
+
+   friend class GFXDevice;
+   friend class GFXGLDevice;
+
+   /// The callback used to get texture events.
+   /// @see GFXTextureManager::addEventDelegate
+   void _onTextureEvent( GFXTexCallbackCode code );
+   
+   GLuint mCubemap; ///< Internal GL handle
+   U32 mDynamicTexSize; ///< Size of faces for a dynamic texture (used in resurrect)
+   
+   // Self explanatory
+   U32 mWidth;
+   U32 mHeight;
+   GFXFormat mFaceFormat;
+      
+   GFXTexHandle mTextures[6]; ///< Keep refs to our textures for resurrection of static cubemaps
+   
+   /// The backing DDSFile uses to restore the faces
+   /// when the surface is lost.
+   Resource<DDSFile> mDDSFile;
+
+   // should only be called by GFXDevice
+   virtual void setToTexUnit( U32 tuNum ); ///< Binds the cubemap to the given texture unit
+   virtual void bind(U32 textureUnit) const; ///< Notifies our owning device that we want to be set to the given texture unit (used for GL internal state tracking)
+   void fillCubeTextures(GFXTexHandle* faces); ///< Copies the textures in faces into the cubemap
+   
+};
+
+class GFXGLCubemapArray : public GFXCubemapArray
+{
+public:
+   GFXGLCubemapArray();
+   virtual ~GFXGLCubemapArray();
+   virtual void initStatic(GFXCubemapHandle *cubemaps, const U32 cubemapCount);
+   virtual void setToTexUnit(U32 tuNum);
+
+   // GFXResource interface
+   virtual void zombify() {}
+   virtual void resurrect() {}
+
+protected:
+   friend class GFXGLDevice;
+   void bind(U32 textureUnit) const;
+   GLuint mCubemap; ///< Internal GL handle
+
+};
+
+#endif

+ 1029 - 0
gl/gfxGLDevice.cpp

@@ -0,0 +1,1029 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "gfx/gl/gfxGLDevice.h"
+#include "platform/platformGL.h"
+
+#include "gfx/gfxCubemap.h"
+#include "gfx/screenshot.h"
+#include "gfx/gfxDrawUtil.h"
+
+#include "gfx/gl/gfxGLEnumTranslate.h"
+#include "gfx/gl/gfxGLVertexBuffer.h"
+#include "gfx/gl/gfxGLPrimitiveBuffer.h"
+#include "gfx/gl/gfxGLTextureTarget.h"
+#include "gfx/gl/gfxGLTextureManager.h"
+#include "gfx/gl/gfxGLTextureObject.h"
+#include "gfx/gl/gfxGLCubemap.h"
+#include "gfx/gl/gfxGLCardProfiler.h"
+#include "gfx/gl/gfxGLWindowTarget.h"
+#include "platform/platformDlibrary.h"
+#include "gfx/gl/gfxGLShader.h"
+#include "gfx/primBuilder.h"
+#include "console/console.h"
+#include "gfx/gl/gfxGLOcclusionQuery.h"
+#include "materials/shaderData.h"
+#include "gfx/gl/gfxGLStateCache.h"
+#include "gfx/gl/gfxGLVertexAttribLocation.h"
+#include "gfx/gl/gfxGLVertexDecl.h"
+#include "shaderGen/shaderGen.h"
+
+GFXAdapter::CreateDeviceInstanceDelegate GFXGLDevice::mCreateDeviceInstance(GFXGLDevice::createInstance); 
+
+GFXDevice *GFXGLDevice::createInstance( U32 adapterIndex )
+{
+   return new GFXGLDevice(adapterIndex);
+}
+
+namespace GL
+{
+   extern void gglPerformBinds();
+   extern void gglPerformExtensionBinds(void *context);
+}
+
+void loadGLCore()
+{
+   static bool coreLoaded = false; // Guess what this is for.
+   if(coreLoaded)
+      return;
+   coreLoaded = true;
+   
+   // Make sure we've got our GL bindings.
+   GL::gglPerformBinds();
+}
+
+void loadGLExtensions(void *context)
+{
+   static bool extensionsLoaded = false;
+   if(extensionsLoaded)
+      return;
+   extensionsLoaded = true;
+   
+   GL::gglPerformExtensionBinds(context);
+}
+
+void STDCALL glDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, 
+	const GLchar *message, const void *userParam)
+{
+    // JTH [11/24/2016]: This is a temporary fix so that we do not get spammed for redundant fbo changes.
+    // This only happens on Intel cards. This should be looked into sometime in the near future.
+    if (dStrStartsWith(message, "API_ID_REDUNDANT_FBO"))
+        return;
+    if (severity == GL_DEBUG_SEVERITY_HIGH)
+        Con::errorf("OPENGL: %s", message);
+    else if (severity == GL_DEBUG_SEVERITY_MEDIUM)
+        Con::warnf("OPENGL: %s", message);
+    else if (severity == GL_DEBUG_SEVERITY_LOW)
+        Con::printf("OPENGL: %s", message);
+}
+
+void STDCALL glAmdDebugCallback(GLuint id, GLenum category, GLenum severity, GLsizei length,
+    const GLchar* message, GLvoid* userParam)
+{
+    if (severity == GL_DEBUG_SEVERITY_HIGH)
+        Con::errorf("AMDOPENGL: %s", message);
+    else if (severity == GL_DEBUG_SEVERITY_MEDIUM)
+        Con::warnf("AMDOPENGL: %s", message);
+    else if (severity == GL_DEBUG_SEVERITY_LOW)
+        Con::printf("AMDOPENGL: %s", message);
+}
+
+void GFXGLDevice::initGLState()
+{  
+   // We don't currently need to sync device state with a known good place because we are
+   // going to set everything in GFXGLStateBlock, but if we change our GFXGLStateBlock strategy, this may
+   // need to happen.
+   
+   // Deal with the card profiler here when we know we have a valid context.
+   mCardProfiler = new GFXGLCardProfiler();
+   mCardProfiler->init(); 
+   glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (GLint*)&mMaxShaderTextures);
+   // JTH: Needs removed, ffp
+   //glGetIntegerv(GL_MAX_TEXTURE_UNITS, (GLint*)&mMaxFFTextures);
+   glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, (GLint*)&mMaxTRColors);
+   mMaxTRColors = getMin( mMaxTRColors, (U32)(GFXTextureTarget::MaxRenderSlotId-1) );
+   
+   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+   
+   // [JTH 5/6/2016] GLSL 1.50 is really SM 4.0
+   // Setting mPixelShaderVersion to 3.0 will allow Advanced Lighting to run.   
+   mPixelShaderVersion = 3.0;
+
+	// Set capability extensions.
+   mCapabilities.anisotropicFiltering = mCardProfiler->queryProfile("GL_EXT_texture_filter_anisotropic");
+   mCapabilities.bufferStorage = mCardProfiler->queryProfile("GL_ARB_buffer_storage");
+   mCapabilities.shaderModel5 = mCardProfiler->queryProfile("GL_ARB_gpu_shader5");
+   mCapabilities.textureStorage = mCardProfiler->queryProfile("GL_ARB_texture_storage");
+   mCapabilities.samplerObjects = mCardProfiler->queryProfile("GL_ARB_sampler_objects");
+   mCapabilities.copyImage = mCardProfiler->queryProfile("GL_ARB_copy_image");
+   mCapabilities.vertexAttributeBinding = mCardProfiler->queryProfile("GL_ARB_vertex_attrib_binding");
+
+   String vendorStr = (const char*)glGetString( GL_VENDOR );
+   if( vendorStr.find("NVIDIA", 0, String::NoCase | String::Left) != String::NPos)
+      mUseGlMap = false;
+   
+   // Workaround for all Mac's, has a problem using glMap* with volatile buffers
+#ifdef TORQUE_OS_MAC
+   mUseGlMap = false;
+#endif
+
+#if TORQUE_DEBUG
+   if( gglHasExtension(ARB_debug_output) )
+   {
+      glEnable(GL_DEBUG_OUTPUT);
+      glDebugMessageCallbackARB(glDebugCallback, NULL);
+      glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
+      GLuint unusedIds = 0;
+      glDebugMessageControlARB(GL_DONT_CARE,
+            GL_DONT_CARE,
+            GL_DONT_CARE,
+            0,
+            &unusedIds,
+            GL_TRUE);
+   }
+   else if(gglHasExtension(AMD_debug_output))
+   {
+      glEnable(GL_DEBUG_OUTPUT);
+      glDebugMessageCallbackAMD(glAmdDebugCallback, NULL);      
+      //glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
+      GLuint unusedIds = 0;
+      glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0,&unusedIds, GL_TRUE);
+   }
+#endif
+
+   PlatformGL::setVSync(smDisableVSync ? 0 : 1);
+   
+   //install vsync callback
+   Con::NotifyDelegate clbk( this, &GFXGLDevice::vsyncCallback );
+   Con::addVariableNotify( "$pref::Video::disableVerticalSync", clbk );
+
+   //OpenGL 3 need a binded VAO for render
+   GLuint vao;
+   glGenVertexArrays(1, &vao);
+   glBindVertexArray(vao);
+
+   //enable sRGB
+   glEnable(GL_FRAMEBUFFER_SRGB);
+}
+
+void GFXGLDevice::vsyncCallback()
+{
+   PlatformGL::setVSync(smDisableVSync ? 0 : 1);
+}
+
+GFXGLDevice::GFXGLDevice(U32 adapterIndex) :
+   mAdapterIndex(adapterIndex),
+   mNeedUpdateVertexAttrib(false),
+   mCurrentPB(NULL),
+   mDrawInstancesCount(0),
+   mCurrentShader( NULL ),
+   m_mCurrentWorld(true),
+   m_mCurrentView(true),
+   mContext(NULL),
+   mPixelFormat(NULL),
+   mPixelShaderVersion(0.0f),
+   mMaxShaderTextures(2),
+   mMaxFFTextures(2),
+   mMaxTRColors(1),
+   mClip(0, 0, 0, 0),
+   mWindowRT(NULL),
+   mUseGlMap(true)
+{
+   for(int i = 0; i < VERTEX_STREAM_COUNT; ++i)
+   {
+      mCurrentVB[i] = NULL;
+      mCurrentVB_Divisor[i] = 0;
+   }
+
+   // Initiailize capabilities to false.
+   memset(&mCapabilities, 0, sizeof(GLCapabilities));
+
+   loadGLCore();
+
+   GFXGLEnumTranslate::init();
+
+   GFXVertexColor::setSwizzle( &Swizzles::rgba );
+
+   // OpenGL have native RGB, no need swizzle
+   mDeviceSwizzle32 = &Swizzles::rgba;
+   mDeviceSwizzle24 = &Swizzles::rgb;
+
+   mTextureManager = new GFXGLTextureManager();
+   gScreenShot = new ScreenShot();
+
+   for(U32 i = 0; i < TEXTURE_STAGE_COUNT; i++)
+      mActiveTextureType[i] = GL_ZERO;
+
+   mNumVertexStream = 2;
+
+   for(int i = 0; i < GS_COUNT; ++i)
+      mModelViewProjSC[i] = NULL;
+
+   mOpenglStateCache = new GFXGLStateCache;
+
+}
+
+GFXGLDevice::~GFXGLDevice()
+{
+   mCurrentStateBlock = NULL;
+
+   for(int i = 0; i < VERTEX_STREAM_COUNT; ++i)      
+      mCurrentVB[i] = NULL;
+   mCurrentPB = NULL;
+   
+   for(U32 i = 0; i < mVolatileVBs.size(); i++)
+      mVolatileVBs[i] = NULL;
+   for(U32 i = 0; i < mVolatilePBs.size(); i++)
+      mVolatilePBs[i] = NULL;
+
+   // Clear out our current texture references
+   for (U32 i = 0; i < TEXTURE_STAGE_COUNT; i++)
+   {
+      mCurrentTexture[i] = NULL;
+      mNewTexture[i] = NULL;
+      mCurrentCubemap[i] = NULL;
+      mNewCubemap[i] = NULL;
+   }
+
+   mRTStack.clear();
+   mCurrentRT = NULL;
+
+   if( mTextureManager )
+   {
+      mTextureManager->zombify();
+      mTextureManager->kill();
+   }
+
+   GFXResource* walk = mResourceListHead;
+   while(walk)
+   {
+      walk->zombify();
+      walk = walk->getNextResource();
+   }
+      
+   if( mCardProfiler )
+      SAFE_DELETE( mCardProfiler );
+
+   SAFE_DELETE( gScreenShot );
+
+   SAFE_DELETE( mOpenglStateCache );
+}
+
+void GFXGLDevice::zombify()
+{
+   mTextureManager->zombify();
+
+   for(int i = 0; i < VERTEX_STREAM_COUNT; ++i)   
+      if(mCurrentVB[i])
+         mCurrentVB[i]->finish();
+   if(mCurrentPB)
+         mCurrentPB->finish();
+   
+   //mVolatileVBs.clear();
+   //mVolatilePBs.clear();
+   GFXResource* walk = mResourceListHead;
+   while(walk)
+   {
+      walk->zombify();
+      walk = walk->getNextResource();
+   }
+}
+
+void GFXGLDevice::resurrect()
+{
+   GFXResource* walk = mResourceListHead;
+   while(walk)
+   {
+      walk->resurrect();
+      walk = walk->getNextResource();
+   }
+   for(int i = 0; i < VERTEX_STREAM_COUNT; ++i)   
+      if(mCurrentVB[i])
+         mCurrentVB[i]->prepare();
+   if(mCurrentPB)
+      mCurrentPB->prepare();
+   
+   mTextureManager->resurrect();
+}
+
+GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize)
+{
+   PROFILE_SCOPE(GFXGLDevice_findVBPool);
+   for(U32 i = 0; i < mVolatileVBs.size(); i++)
+      if (  mVolatileVBs[i]->mNumVerts >= numVerts &&
+            mVolatileVBs[i]->mVertexFormat.isEqual( *vertexFormat ) &&
+            mVolatileVBs[i]->mVertexSize == vertSize &&
+            mVolatileVBs[i]->getRefCount() == 1 )
+         return mVolatileVBs[i];
+
+   // No existing VB, so create one
+   PROFILE_SCOPE(GFXGLDevice_createVBPool);
+   StrongRefPtr<GFXGLVertexBuffer> buf(new GFXGLVertexBuffer(GFX, numVerts, vertexFormat, vertSize, GFXBufferTypeVolatile));
+   buf->registerResourceWithDevice(this);
+   mVolatileVBs.push_back(buf);
+   return buf.getPointer();
+}
+
+GFXPrimitiveBuffer* GFXGLDevice::findVolatilePBO(U32 numIndices, U32 numPrimitives)
+{
+   for(U32 i = 0; i < mVolatilePBs.size(); i++)
+      if((mVolatilePBs[i]->mIndexCount >= numIndices) && (mVolatilePBs[i]->getRefCount() == 1))
+         return mVolatilePBs[i];
+   
+   // No existing PB, so create one
+   StrongRefPtr<GFXGLPrimitiveBuffer> buf(new GFXGLPrimitiveBuffer(GFX, numIndices, numPrimitives, GFXBufferTypeVolatile));
+   buf->registerResourceWithDevice(this);
+   mVolatilePBs.push_back(buf);
+   return buf.getPointer();
+}
+
+GFXVertexBuffer *GFXGLDevice::allocVertexBuffer(   U32 numVerts, 
+                                                   const GFXVertexFormat *vertexFormat, 
+                                                   U32 vertSize, 
+                                                   GFXBufferType bufferType,
+                                                   void* data )  
+{
+   PROFILE_SCOPE(GFXGLDevice_allocVertexBuffer);
+   if(bufferType == GFXBufferTypeVolatile)
+      return findVolatileVBO(numVerts, vertexFormat, vertSize);
+         
+   GFXGLVertexBuffer* buf = new GFXGLVertexBuffer( GFX, numVerts, vertexFormat, vertSize, bufferType );
+   buf->registerResourceWithDevice(this);   
+
+   if(data)
+   {
+      void* dest;
+      buf->lock(0, numVerts, &dest);
+      dMemcpy(dest, data, vertSize * numVerts);
+      buf->unlock();
+   }
+
+   return buf;
+}
+
+GFXPrimitiveBuffer *GFXGLDevice::allocPrimitiveBuffer( U32 numIndices, U32 numPrimitives, GFXBufferType bufferType, void* data ) 
+{
+   GFXPrimitiveBuffer* buf;
+   
+   if(bufferType == GFXBufferTypeVolatile)
+   {
+      buf = findVolatilePBO(numIndices, numPrimitives);
+   }
+   else
+   {
+      buf = new GFXGLPrimitiveBuffer(GFX, numIndices, numPrimitives, bufferType);
+      buf->registerResourceWithDevice(this);
+   }
+   
+   if(data)
+   {
+      void* dest;
+      buf->lock(0, numIndices, &dest);
+      dMemcpy(dest, data, sizeof(U16) * numIndices);
+      buf->unlock();
+   }
+   return buf;
+}
+
+void GFXGLDevice::setVertexStream( U32 stream, GFXVertexBuffer *buffer )
+{
+   AssertFatal(stream <= 1, "GFXGLDevice::setVertexStream only support 2 stream (0: data, 1: instancing)");
+
+   //if(mCurrentVB[stream] != buffer)
+   {
+      // Reset the state the old VB required, then set the state the new VB requires.
+      if( mCurrentVB[stream] )
+      {     
+         mCurrentVB[stream]->finish();
+      }
+
+      mCurrentVB[stream] = static_cast<GFXGLVertexBuffer*>( buffer );
+
+      mNeedUpdateVertexAttrib = true;
+   }
+}
+
+void GFXGLDevice::setVertexStreamFrequency( U32 stream, U32 frequency )
+{
+   if( stream == 0 )
+   {
+      mCurrentVB_Divisor[stream] = 0; // non instanced, is vertex buffer
+      mDrawInstancesCount = frequency; // instances count
+   }
+   else
+   {
+      AssertFatal(frequency <= 1, "GFXGLDevice::setVertexStreamFrequency only support 0/1 for this stream" );
+      if( stream == 1 && frequency == 1 )
+         mCurrentVB_Divisor[stream] = 1; // instances data need a frequency of 1
+      else
+         mCurrentVB_Divisor[stream] = 0;
+   }
+
+   mNeedUpdateVertexAttrib = true;
+}
+
+GFXCubemap* GFXGLDevice::createCubemap()
+{ 
+   GFXGLCubemap* cube = new GFXGLCubemap();
+   cube->registerResourceWithDevice(this);
+   return cube; 
+};
+
+GFXCubemapArray *GFXGLDevice::createCubemapArray()
+{
+   GFXGLCubemapArray* cubeArray = new GFXGLCubemapArray();
+   cubeArray->registerResourceWithDevice(this);
+   return cubeArray;
+}
+
+void GFXGLDevice::endSceneInternal() 
+{
+   // nothing to do for opengl
+   mCanCurrentlyRender = false;
+}
+
+void GFXGLDevice::clear(U32 flags, const LinearColorF& color, F32 z, U32 stencil)
+{
+   // Make sure we have flushed our render target state.
+   _updateRenderTargets();
+   
+   bool writeAllColors = true;
+   bool zwrite = true;   
+   bool writeAllStencil = true;
+   const GFXStateBlockDesc *desc = NULL;
+   if (mCurrentGLStateBlock)
+   {
+      desc = &mCurrentGLStateBlock->getDesc();
+      zwrite = desc->zWriteEnable;
+      writeAllColors = desc->colorWriteRed && desc->colorWriteGreen && desc->colorWriteBlue && desc->colorWriteAlpha;
+      writeAllStencil = desc->stencilWriteMask == 0xFFFFFFFF;
+   }
+   
+   glColorMask(true, true, true, true);
+   glDepthMask(true);
+   glStencilMask(0xFFFFFFFF);
+   glClearColor(color.red, color.green, color.blue, color.alpha);
+   glClearDepth(z);
+   glClearStencil(stencil);
+
+   GLbitfield clearflags = 0;
+   clearflags |= (flags & GFXClearTarget)   ? GL_COLOR_BUFFER_BIT : 0;
+   clearflags |= (flags & GFXClearZBuffer)  ? GL_DEPTH_BUFFER_BIT : 0;
+   clearflags |= (flags & GFXClearStencil)  ? GL_STENCIL_BUFFER_BIT : 0;
+
+   glClear(clearflags);
+
+   if(!writeAllColors)
+      glColorMask(desc->colorWriteRed, desc->colorWriteGreen, desc->colorWriteBlue, desc->colorWriteAlpha);
+   
+   if(!zwrite)
+      glDepthMask(false);
+
+   if(!writeAllStencil)
+      glStencilMask(desc->stencilWriteMask);
+}
+
+void GFXGLDevice::clearColorAttachment(const U32 attachment, const LinearColorF& color)
+{
+   const GLfloat clearColor[4] = { color.red, color.green, color.blue, color.alpha };
+   glClearBufferfv(GL_COLOR, attachment, clearColor);
+}
+
+// Given a primitive type and a number of primitives, return the number of indexes/vertexes used.
+inline GLsizei GFXGLDevice::primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount)
+{
+   switch (primType)
+   {
+      case GFXPointList :
+         return primitiveCount;
+         break;
+      case GFXLineList :
+         return primitiveCount * 2;
+         break;
+      case GFXLineStrip :
+         return primitiveCount + 1;
+         break;
+      case GFXTriangleList :
+         return primitiveCount * 3;
+         break;
+      case GFXTriangleStrip :
+         return 2 + primitiveCount;
+         break;
+      default:
+         AssertFatal(false, "GFXGLDevice::primCountToIndexCount - unrecognized prim type");
+         break;
+   }
+   
+   return 0;
+}
+
+GFXVertexDecl* GFXGLDevice::allocVertexDecl( const GFXVertexFormat *vertexFormat ) 
+{
+   PROFILE_SCOPE(GFXGLDevice_allocVertexDecl);
+   typedef Map<void*, GFXGLVertexDecl> GFXGLVertexDeclMap;
+   static GFXGLVertexDeclMap declMap;   
+   GFXGLVertexDeclMap::Iterator itr = declMap.find( (void*)vertexFormat->getDescription().c_str() ); // description string are interned, safe to use c_str()
+   if(itr != declMap.end())
+      return &itr->value;
+
+   GFXGLVertexDecl &decl = declMap[(void*)vertexFormat->getDescription().c_str()];   
+   decl.init(vertexFormat);
+   return &decl;
+}
+
+void GFXGLDevice::setVertexDecl( const GFXVertexDecl *decl )
+{
+   static_cast<const GFXGLVertexDecl*>(decl)->prepareVertexFormat();
+}
+
+inline void GFXGLDevice::preDrawPrimitive()
+{
+   if( mStateDirty )
+   {
+      updateStates();
+   }
+   
+   if(mCurrentShaderConstBuffer)
+      setShaderConstBufferInternal(mCurrentShaderConstBuffer);
+
+   if( mNeedUpdateVertexAttrib )
+   {
+      AssertFatal(mCurrVertexDecl, "");
+      const GFXGLVertexDecl* decl = static_cast<const GFXGLVertexDecl*>(mCurrVertexDecl);
+      
+      for(int i = 0; i < getNumVertexStreams(); ++i)
+      {
+         if(mCurrentVB[i])
+         {
+            mCurrentVB[i]->prepare(i, mCurrentVB_Divisor[i]);    // GL_ARB_vertex_attrib_binding  
+            decl->prepareBuffer_old( i, mCurrentVB[i]->mBuffer, mCurrentVB_Divisor[i] ); // old vertex buffer/format
+         }
+      }
+
+      decl->updateActiveVertexAttrib( GFXGL->getOpenglCache()->getCacheVertexAttribActive() );         
+   }   
+
+   mNeedUpdateVertexAttrib = false;
+}
+
+inline void GFXGLDevice::postDrawPrimitive(U32 primitiveCount)
+{
+   mDeviceStatistics.mDrawCalls++;
+   mDeviceStatistics.mPolyCount += primitiveCount;
+}
+
+void GFXGLDevice::drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ) 
+{
+   preDrawPrimitive();
+  
+   if(mCurrentVB[0])
+      vertexStart += mCurrentVB[0]->mBufferVertexOffset;
+
+   if(mDrawInstancesCount)
+      glDrawArraysInstanced(GFXGLPrimType[primType], vertexStart, primCountToIndexCount(primType, primitiveCount), mDrawInstancesCount);
+   else
+      glDrawArrays(GFXGLPrimType[primType], vertexStart, primCountToIndexCount(primType, primitiveCount));   
+
+   postDrawPrimitive(primitiveCount);
+}
+
+void GFXGLDevice::drawIndexedPrimitive(   GFXPrimitiveType primType, 
+                                          U32 startVertex, 
+                                          U32 minIndex, 
+                                          U32 numVerts, 
+                                          U32 startIndex, 
+                                          U32 primitiveCount )
+{
+   preDrawPrimitive();
+
+   U16* buf = (U16*)static_cast<GFXGLPrimitiveBuffer*>(mCurrentPrimitiveBuffer.getPointer())->getBuffer() + startIndex + mCurrentPrimitiveBuffer->mVolatileStart;
+
+   const U32 baseVertex = mCurrentVB[0]->mBufferVertexOffset + startVertex;
+
+   if(mDrawInstancesCount)
+      glDrawElementsInstancedBaseVertex(GFXGLPrimType[primType], primCountToIndexCount(primType, primitiveCount), GL_UNSIGNED_SHORT, buf, mDrawInstancesCount, baseVertex);
+   else
+      glDrawElementsBaseVertex(GFXGLPrimType[primType], primCountToIndexCount(primType, primitiveCount), GL_UNSIGNED_SHORT, buf, baseVertex);
+
+   postDrawPrimitive(primitiveCount);
+}
+
+void GFXGLDevice::setPB(GFXGLPrimitiveBuffer* pb)
+{
+   if(mCurrentPB)
+      mCurrentPB->finish();
+   mCurrentPB = pb;
+}
+
+void GFXGLDevice::setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable)
+{
+   // ONLY NEEDED ON FFP
+}
+
+void GFXGLDevice::setLightMaterialInternal(const GFXLightMaterial mat)
+{
+   // ONLY NEEDED ON FFP
+}
+
+void GFXGLDevice::setGlobalAmbientInternal(LinearColorF color)
+{
+   // ONLY NEEDED ON FFP
+}
+
+void GFXGLDevice::setTextureInternal(U32 textureUnit, const GFXTextureObject*texture)
+{
+   GFXGLTextureObject *tex = static_cast<GFXGLTextureObject*>(const_cast<GFXTextureObject*>(texture));
+   if (tex)
+   {
+      mActiveTextureType[textureUnit] = tex->getBinding();
+      tex->bind(textureUnit);
+   } 
+   else if(mActiveTextureType[textureUnit] != GL_ZERO)
+   {
+      glActiveTexture(GL_TEXTURE0 + textureUnit);
+      glBindTexture(mActiveTextureType[textureUnit], 0);
+      getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0);
+      mActiveTextureType[textureUnit] = GL_ZERO;
+   }
+}
+
+void GFXGLDevice::setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture)
+{
+   if(texture)
+   {
+      mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP;
+      texture->bind(textureUnit);
+   }
+   else if(mActiveTextureType[textureUnit] != GL_ZERO)
+   {
+      glActiveTexture(GL_TEXTURE0 + textureUnit);
+      glBindTexture(mActiveTextureType[textureUnit], 0);
+      getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0);
+      mActiveTextureType[textureUnit] = GL_ZERO;
+   }
+}
+
+void GFXGLDevice::setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture)
+{
+   if (texture)
+   {
+      mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP_ARRAY;
+      texture->bind(textureUnit);
+   }
+   else if (mActiveTextureType[textureUnit] != GL_ZERO)
+   {
+      glActiveTexture(GL_TEXTURE0 + textureUnit);
+      glBindTexture(mActiveTextureType[textureUnit], 0);
+      getOpenglCache()->setCacheBindedTex(textureUnit, mActiveTextureType[textureUnit], 0);
+      mActiveTextureType[textureUnit] = GL_ZERO;
+   }
+}
+
+void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat )
+{
+   // ONLY NEEDED ON FFP
+}
+
+void GFXGLDevice::setClipRect( const RectI &inRect )
+{
+   AssertFatal(mCurrentRT.isValid(), "GFXGLDevice::setClipRect - must have a render target set to do any rendering operations!");
+
+   // Clip the rect against the renderable size.
+   Point2I size = mCurrentRT->getSize();
+   RectI maxRect(Point2I(0,0), size);
+   mClip = inRect;
+   mClip.intersect(maxRect);
+
+   // Create projection matrix.  See http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/ortho.html
+   const F32 left = mClip.point.x;
+   const F32 right = mClip.point.x + mClip.extent.x;
+   const F32 bottom = mClip.extent.y;
+   const F32 top = 0.0f;
+   const F32 nearPlane = 0.0f;
+   const F32 farPlane = 1.0f;
+   
+   const F32 tx = -(right + left)/(right - left);
+   const F32 ty = -(top + bottom)/(top - bottom);
+   const F32 tz = -(farPlane + nearPlane)/(farPlane - nearPlane);
+   
+   static Point4F pt;
+   pt.set(2.0f / (right - left), 0.0f, 0.0f, 0.0f);
+   mProjectionMatrix.setColumn(0, pt);
+   
+   pt.set(0.0f, 2.0f/(top - bottom), 0.0f, 0.0f);
+   mProjectionMatrix.setColumn(1, pt);
+   
+   pt.set(0.0f, 0.0f, -2.0f/(farPlane - nearPlane), 0.0f);
+   mProjectionMatrix.setColumn(2, pt);
+   
+   pt.set(tx, ty, tz, 1.0f);
+   mProjectionMatrix.setColumn(3, pt);
+   
+   // Translate projection matrix.
+   static MatrixF translate(true);
+   pt.set(0.0f, -mClip.point.y, 0.0f, 1.0f);
+   translate.setColumn(3, pt);
+   
+   mProjectionMatrix *= translate;
+   
+   setMatrix(GFXMatrixProjection, mProjectionMatrix);
+   
+   MatrixF mTempMatrix(true);
+   setViewMatrix( mTempMatrix );
+   setWorldMatrix( mTempMatrix );
+
+   // Set the viewport to the clip rect
+   RectI viewport(mClip.point.x, mClip.point.y, mClip.extent.x, mClip.extent.y);
+   setViewport(viewport);
+}
+
+/// Creates a state block object based on the desc passed in.  This object
+/// represents an immutable state.
+GFXStateBlockRef GFXGLDevice::createStateBlockInternal(const GFXStateBlockDesc& desc)
+{
+   return GFXStateBlockRef(new GFXGLStateBlock(desc));
+}
+
+/// Activates a stateblock
+void GFXGLDevice::setStateBlockInternal(GFXStateBlock* block, bool force)
+{
+   AssertFatal(dynamic_cast<GFXGLStateBlock*>(block), "GFXGLDevice::setStateBlockInternal - Incorrect stateblock type for this device!");
+   GFXGLStateBlock* glBlock = static_cast<GFXGLStateBlock*>(block);
+   GFXGLStateBlock* glCurrent = static_cast<GFXGLStateBlock*>(mCurrentStateBlock.getPointer());
+   if (force)
+      glCurrent = NULL;
+      
+   glBlock->activate(glCurrent); // Doesn't use current yet.
+   mCurrentGLStateBlock = glBlock;
+}
+
+//------------------------------------------------------------------------------
+
+GFXTextureTarget * GFXGLDevice::allocRenderToTextureTarget(bool genMips)
+{
+   GFXGLTextureTarget *targ = new GFXGLTextureTarget(genMips);
+   targ->registerResourceWithDevice(this);
+   return targ;
+}
+
+GFXFence * GFXGLDevice::createFence()
+{
+   GFXFence* fence = _createPlatformSpecificFence();
+   if(!fence)
+      fence = new GFXGeneralFence( this );
+      
+   fence->registerResourceWithDevice(this);
+   return fence;
+}
+
+GFXOcclusionQuery* GFXGLDevice::createOcclusionQuery()
+{   
+   GFXOcclusionQuery *query = new GFXGLOcclusionQuery( this );
+   query->registerResourceWithDevice(this);
+   return query;
+}
+
+void GFXGLDevice::setupGenericShaders( GenericShaderType type ) 
+{
+   AssertFatal(type != GSTargetRestore, "");
+
+   if( mGenericShader[GSColor] == NULL )
+   {
+      ShaderData *shaderData;
+
+      shaderData = new ShaderData();
+      shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/colorV.glsl"));
+      shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/colorP.glsl"));
+      shaderData->setField("pixVersion", "2.0");
+      shaderData->registerObject();
+      mGenericShader[GSColor] =  shaderData->getShader();
+      mGenericShaderBuffer[GSColor] = mGenericShader[GSColor]->allocConstBuffer();
+      mModelViewProjSC[GSColor] = mGenericShader[GSColor]->getShaderConstHandle( "$modelView" );
+      Sim::getRootGroup()->addObject(shaderData);
+
+      shaderData = new ShaderData();
+      shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/modColorTextureV.glsl"));
+      shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/modColorTextureP.glsl"));
+      shaderData->setSamplerName("$diffuseMap", 0);
+      shaderData->setField("pixVersion", "2.0");
+      shaderData->registerObject();
+      mGenericShader[GSModColorTexture] = shaderData->getShader();
+      mGenericShaderBuffer[GSModColorTexture] = mGenericShader[GSModColorTexture]->allocConstBuffer();
+      mModelViewProjSC[GSModColorTexture] = mGenericShader[GSModColorTexture]->getShaderConstHandle( "$modelView" );
+      Sim::getRootGroup()->addObject(shaderData);
+
+      shaderData = new ShaderData();
+      shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/addColorTextureV.glsl"));
+      shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/addColorTextureP.glsl"));
+      shaderData->setSamplerName("$diffuseMap", 0);
+      shaderData->setField("pixVersion", "2.0");
+      shaderData->registerObject();
+      mGenericShader[GSAddColorTexture] = shaderData->getShader();
+      mGenericShaderBuffer[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->allocConstBuffer();
+      mModelViewProjSC[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->getShaderConstHandle( "$modelView" );
+      Sim::getRootGroup()->addObject(shaderData);
+
+      shaderData = new ShaderData();
+      shaderData->setField("OGLVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/textureV.glsl"));
+      shaderData->setField("OGLPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/gl/textureP.glsl"));
+      shaderData->setSamplerName("$diffuseMap", 0);
+      shaderData->setField("pixVersion", "2.0");
+      shaderData->registerObject();
+      mGenericShader[GSTexture] = shaderData->getShader();
+      mGenericShaderBuffer[GSTexture] = mGenericShader[GSTexture]->allocConstBuffer();
+      mModelViewProjSC[GSTexture] = mGenericShader[GSTexture]->getShaderConstHandle( "$modelView" );
+      Sim::getRootGroup()->addObject(shaderData);
+   }
+
+   MatrixF tempMatrix =  mProjectionMatrix * mViewMatrix * mWorldMatrix[mWorldStackSize];  
+   mGenericShaderBuffer[type]->setSafe(mModelViewProjSC[type], tempMatrix);
+
+   setShader( mGenericShader[type] );
+   setShaderConstBuffer( mGenericShaderBuffer[type] );
+}
+GFXShader* GFXGLDevice::createShader()
+{
+   GFXGLShader* shader = new GFXGLShader();
+   shader->registerResourceWithDevice( this );
+   return shader;
+}
+
+void GFXGLDevice::setShader(GFXShader *shader, bool force)
+{
+   if(mCurrentShader == shader && !force)
+      return;
+
+   if ( shader )
+   {
+      GFXGLShader *glShader = static_cast<GFXGLShader*>( shader );
+      glShader->useProgram();
+      mCurrentShader = shader;
+   }
+   else
+   {
+      setupGenericShaders();
+   }
+}
+
+void GFXGLDevice::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer)
+{
+   PROFILE_SCOPE(GFXGLDevice_setShaderConstBufferInternal);
+   static_cast<GFXGLShaderConstBuffer*>(buffer)->activate();
+}
+
+U32 GFXGLDevice::getNumSamplers() const
+{
+   return getMin((U32)TEXTURE_STAGE_COUNT,mPixelShaderVersion > 0.001f ? mMaxShaderTextures : mMaxFFTextures);
+}
+
+GFXTextureObject* GFXGLDevice::getDefaultDepthTex() const 
+{
+   if(mWindowRT && mWindowRT->getPointer())
+      return static_cast<GFXGLWindowTarget*>( mWindowRT->getPointer() )->mBackBufferDepthTex.getPointer();
+
+   return NULL;
+}
+
+U32 GFXGLDevice::getNumRenderTargets() const 
+{ 
+   return mMaxTRColors; 
+}
+
+void GFXGLDevice::_updateRenderTargets()
+{
+   if ( mRTDirty || mCurrentRT->isPendingState() )
+   {
+      if ( mRTDeactivate )
+      {
+         mRTDeactivate->deactivate();
+         mRTDeactivate = NULL;   
+      }
+      
+      // NOTE: The render target changes is not really accurate
+      // as the GFXTextureTarget supports MRT internally.  So when
+      // we activate a GFXTarget it could result in multiple calls
+      // to SetRenderTarget on the actual device.
+      mDeviceStatistics.mRenderTargetChanges++;
+
+      GFXGLTextureTarget *tex = dynamic_cast<GFXGLTextureTarget*>( mCurrentRT.getPointer() );
+      if ( tex )
+      {
+         tex->applyState();
+         tex->makeActive();
+      }
+      else
+      {
+         GFXGLWindowTarget *win = dynamic_cast<GFXGLWindowTarget*>( mCurrentRT.getPointer() );
+         AssertFatal( win != NULL, 
+                     "GFXGLDevice::_updateRenderTargets() - invalid target subclass passed!" );
+         
+         win->makeActive();
+         
+         if( win->mContext != static_cast<GFXGLDevice*>(GFX)->mContext )
+         {
+            mRTDirty = false;
+            GFX->updateStates(true);
+         }
+      }
+      
+      mRTDirty = false;
+   }
+   
+   if ( mViewportDirty )
+   {
+      glViewport( mViewport.point.x, mViewport.point.y, mViewport.extent.x, mViewport.extent.y ); 
+      mViewportDirty = false;
+   }
+}
+
+GFXFormat GFXGLDevice::selectSupportedFormat(   GFXTextureProfile* profile, 
+                                                const Vector<GFXFormat>& formats, 
+                                                bool texture, 
+                                                bool mustblend,
+                                                bool mustfilter )
+{
+   for(U32 i = 0; i < formats.size(); i++)
+   {
+      // Single channel textures are not supported by FBOs.
+      if(profile->testFlag(GFXTextureProfile::RenderTarget) && (formats[i] == GFXFormatA8 || formats[i] == GFXFormatL8 || formats[i] == GFXFormatL16))
+         continue;
+      if(GFXGLTextureInternalFormat[formats[i]] == GL_ZERO)
+         continue;
+      
+      return formats[i];
+   }
+   
+   return GFXFormatR8G8B8A8;
+}
+
+U32 GFXGLDevice::getTotalVideoMemory_GL_EXT()
+{
+   // Source: http://www.opengl.org/registry/specs/ATI/meminfo.txt
+   if( gglHasExtension(ATI_meminfo) )
+   {
+      GLint mem[4] = {0};
+      glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, mem);  // Retrieve the texture pool
+      
+      /* With mem[0] i get only the total memory free in the pool in KB
+      *
+      * mem[0] - total memory free in the pool
+      * mem[1] - largest available free block in the pool
+      * mem[2] - total auxiliary memory free
+      * mem[3] - largest auxiliary free block
+      */
+
+      return  mem[0] / 1024;
+   }
+   
+   //source http://www.opengl.org/registry/specs/NVX/gpu_memory_info.txt
+   else if( gglHasExtension(NVX_gpu_memory_info) )
+   {
+      GLint mem = 0;
+      glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &mem);
+      return mem / 1024;
+   }
+
+   // TODO OPENGL, add supprt for INTEL cards.
+   
+   return 0;
+}
+
+//
+// Register this device with GFXInit
+//
+class GFXGLRegisterDevice
+{
+public:
+   GFXGLRegisterDevice()
+   {
+      GFXInit::getRegisterDeviceSignal().notify(&GFXGLDevice::enumerateAdapters);
+   }
+};
+
+static GFXGLRegisterDevice pGLRegisterDevice;
+
+ConsoleFunction(cycleResources, void, 1, 1, "")
+{
+   static_cast<GFXGLDevice*>(GFX)->zombify();
+   static_cast<GFXGLDevice*>(GFX)->resurrect();
+}

+ 281 - 0
gl/gfxGLDevice.h

@@ -0,0 +1,281 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef _GFXGLDEVICE_H_
+#define _GFXGLDEVICE_H_
+
+#include "platform/platform.h"
+
+#include "gfx/gfxDevice.h"
+#include "gfx/gfxInit.h"
+
+#include "gfx/gl/tGL/tGL.h"
+
+#include "windowManager/platformWindow.h"
+#include "gfx/gfxFence.h"
+#include "gfx/gfxResource.h"
+#include "gfx/gl/gfxGLStateBlock.h"
+
+class GFXGLVertexBuffer;
+class GFXGLPrimitiveBuffer;
+class GFXGLTextureTarget;
+class GFXGLCubemap;
+class GFXGLCubemapArray;
+class GFXGLStateCache;
+class GFXGLVertexDecl;
+
+class GFXGLDevice : public GFXDevice
+{
+public:
+   struct GLCapabilities
+   {
+      bool anisotropicFiltering;
+      bool bufferStorage;
+      bool shaderModel5;
+      bool textureStorage;
+      bool samplerObjects;
+      bool copyImage;
+      bool vertexAttributeBinding;
+   };
+   GLCapabilities mCapabilities;
+
+   void zombify();
+   void resurrect();
+   GFXGLDevice(U32 adapterIndex);
+   virtual ~GFXGLDevice();
+
+   static void enumerateAdapters( Vector<GFXAdapter*> &adapterList );
+   static GFXDevice *createInstance( U32 adapterIndex );
+
+   virtual void init( const GFXVideoMode &mode, PlatformWindow *window = NULL );
+
+   virtual void activate() { }
+   virtual void deactivate() { }
+   virtual GFXAdapterType getAdapterType() { return OpenGL; }
+
+   virtual void enterDebugEvent(ColorI color, const char *name);
+   virtual void leaveDebugEvent();
+   virtual void setDebugMarker(ColorI color, const char *name);
+
+   virtual void enumerateVideoModes();
+
+   virtual U32 getTotalVideoMemory_GL_EXT();
+   virtual U32 getTotalVideoMemory();
+
+   virtual GFXCubemap * createCubemap();
+   virtual GFXCubemapArray *createCubemapArray();
+
+   virtual F32 getFillConventionOffset() const { return 0.0f; }
+
+
+   ///@}
+
+   /// @name Render Target functions
+   /// @{
+
+   ///
+   virtual GFXTextureTarget *allocRenderToTextureTarget(bool genMips = true);
+   virtual GFXWindowTarget *allocWindowTarget(PlatformWindow *window);
+   virtual void _updateRenderTargets();
+
+   ///@}
+
+   /// @name Shader functions
+   /// @{
+   virtual F32 getPixelShaderVersion() const { return mPixelShaderVersion; }
+   virtual void  setPixelShaderVersion( F32 version ) { mPixelShaderVersion = version; }
+   
+   virtual void setShader(GFXShader *shader, bool force = false);
+   
+   /// @attention GL cannot check if the given format supports blending or filtering!
+   virtual GFXFormat selectSupportedFormat(GFXTextureProfile *profile,
+	   const Vector<GFXFormat> &formats, bool texture, bool mustblend, bool mustfilter);
+      
+   /// Returns the number of texture samplers that can be used in a shader rendering pass
+   virtual U32 getNumSamplers() const;
+
+   /// Returns the number of simultaneous render targets supported by the device.
+   virtual U32 getNumRenderTargets() const;
+
+   virtual GFXShader* createShader();
+      
+   virtual void clear( U32 flags, const LinearColorF& color, F32 z, U32 stencil );
+   virtual void clearColorAttachment(const U32 attachment, const LinearColorF& color);
+   virtual bool beginSceneInternal();
+   virtual void endSceneInternal();
+
+   virtual void drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount );
+
+   virtual void drawIndexedPrimitive(  GFXPrimitiveType primType, 
+                                       U32 startVertex, 
+                                       U32 minIndex, 
+                                       U32 numVerts, 
+                                       U32 startIndex, 
+                                       U32 primitiveCount );
+
+   virtual void setClipRect( const RectI &rect );
+   virtual const RectI &getClipRect() const { return mClip; }
+
+   virtual void preDestroy() { Parent::preDestroy(); }
+
+   virtual U32 getMaxDynamicVerts() { return MAX_DYNAMIC_VERTS; }
+   virtual U32 getMaxDynamicIndices() { return MAX_DYNAMIC_INDICES; }
+   
+   GFXFence *createFence();
+   
+   GFXOcclusionQuery* createOcclusionQuery();
+
+   GFXGLStateBlockRef getCurrentStateBlock() { return mCurrentGLStateBlock; }
+   
+   virtual void setupGenericShaders( GenericShaderType type = GSColor );
+   
+   ///
+   bool supportsAnisotropic() const { return mSupportsAnisotropic; }
+
+   GFXGLStateCache* getOpenglCache() { return mOpenglStateCache; }
+
+   GFXTextureObject* getDefaultDepthTex() const;
+
+   /// Returns the number of vertex streams supported by the device.	
+   const U32 getNumVertexStreams() const { return mNumVertexStream; }
+
+   bool glUseMap() const { return mUseGlMap; }   
+      
+protected:   
+   /// Called by GFXDevice to create a device specific stateblock
+   virtual GFXStateBlockRef createStateBlockInternal(const GFXStateBlockDesc& desc);
+   /// Called by GFXDevice to actually set a stateblock.
+   virtual void setStateBlockInternal(GFXStateBlock* block, bool force);   
+
+   /// Called by base GFXDevice to actually set a const buffer
+   virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer);
+
+   virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture);
+   virtual void setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture);
+   virtual void setCubemapArrayInternal(U32 textureUnit, const GFXGLCubemapArray* texture);
+
+   virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable);
+   virtual void setLightMaterialInternal(const GFXLightMaterial mat);
+   virtual void setGlobalAmbientInternal(LinearColorF color);
+
+   /// @name State Initalization.
+   /// @{
+
+   /// State initalization. This MUST BE CALLED in setVideoMode after the device
+   /// is created.
+   virtual void initStates() { }
+
+   virtual void setMatrix( GFXMatrixType mtype, const MatrixF &mat );
+
+   virtual GFXVertexBuffer *allocVertexBuffer(  U32 numVerts, 
+                                                const GFXVertexFormat *vertexFormat,
+                                                U32 vertSize, 
+                                                GFXBufferType bufferType,
+                                                void* data = NULL);
+   virtual GFXPrimitiveBuffer *allocPrimitiveBuffer( U32 numIndices, U32 numPrimitives, GFXBufferType bufferType, void* data = NULL );
+   
+   // NOTE: The GL device doesn't need a vertex declaration at
+   // this time, but we need to return something to keep the system
+   // from retrying to allocate one on every call.
+   virtual GFXVertexDecl* allocVertexDecl( const GFXVertexFormat *vertexFormat );
+
+   virtual void setVertexDecl( const GFXVertexDecl *decl );
+
+   virtual void setVertexStream( U32 stream, GFXVertexBuffer *buffer );
+   virtual void setVertexStreamFrequency( U32 stream, U32 frequency );   
+
+private:
+   typedef GFXDevice Parent;
+   
+   friend class GFXGLTextureObject;
+   friend class GFXGLCubemap;
+   friend class GFXGLCubemapArray;
+   friend class GFXGLWindowTarget;
+   friend class GFXGLPrimitiveBuffer;
+   friend class GFXGLVertexBuffer;
+
+   static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance;
+
+   U32 mAdapterIndex;
+   
+   StrongRefPtr<GFXGLVertexBuffer> mCurrentVB[VERTEX_STREAM_COUNT];
+   U32 mCurrentVB_Divisor[VERTEX_STREAM_COUNT];
+   bool mNeedUpdateVertexAttrib;
+   StrongRefPtr<GFXGLPrimitiveBuffer> mCurrentPB;
+   U32 mDrawInstancesCount;
+   
+   GFXShader* mCurrentShader;
+   GFXShaderRef mGenericShader[GS_COUNT];
+   GFXShaderConstBufferRef mGenericShaderBuffer[GS_COUNT];
+   GFXShaderConstHandle *mModelViewProjSC[GS_COUNT];
+   
+   /// Since GL does not have separate world and view matrices we need to track them
+   MatrixF m_mCurrentWorld;
+   MatrixF m_mCurrentView;
+
+   void* mContext;
+   void* mPixelFormat;
+
+   F32 mPixelShaderVersion;
+   
+   bool mSupportsAnisotropic;   
+
+   U32 mNumVertexStream;
+   
+   U32 mMaxShaderTextures;
+   U32 mMaxFFTextures;
+
+   U32 mMaxTRColors;
+
+   RectI mClip;
+   
+   GFXGLStateBlockRef mCurrentGLStateBlock;
+   
+   GLenum mActiveTextureType[TEXTURE_STAGE_COUNT];
+   
+   Vector< StrongRefPtr<GFXGLVertexBuffer> > mVolatileVBs; ///< Pool of existing volatile VBs so we can reuse previously created ones
+   Vector< StrongRefPtr<GFXGLPrimitiveBuffer> > mVolatilePBs; ///< Pool of existing volatile PBs so we can reuse previously created ones
+
+   GLsizei primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount);
+   void preDrawPrimitive();
+   void postDrawPrimitive(U32 primitiveCount);  
+   
+   GFXVertexBuffer* findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize); ///< Returns an existing volatile VB which has >= numVerts and the same vert flags/size, or creates a new VB if necessary
+   GFXPrimitiveBuffer* findVolatilePBO(U32 numIndices, U32 numPrimitives); ///< Returns an existing volatile PB which has >= numIndices, or creates a new PB if necessary
+
+   void vsyncCallback(); ///< Vsync callback
+   
+   void initGLState(); ///< Guaranteed to be called after all extensions have been loaded, use to init card profiler, shader version, max samplers, etc.
+   
+   GFXFence* _createPlatformSpecificFence(); ///< If our platform (e.g. OS X) supports a fence extenstion (e.g. GL_APPLE_fence) this will create one, otherwise returns NULL
+   
+   void setPB(GFXGLPrimitiveBuffer* pb); ///< Sets mCurrentPB
+
+   GFXGLStateCache *mOpenglStateCache;
+
+   GFXWindowTargetRef *mWindowRT;
+
+   bool mUseGlMap;
+};
+
+#define GFXGL static_cast<GFXGLDevice*>(GFXDevice::get())
+#endif

+ 1151 - 0
gl/gfxGLShader.cpp

@@ -0,0 +1,1151 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "gfx/gl/gfxGLShader.h"
+#include "gfx/gl/gfxGLVertexAttribLocation.h"
+#include "gfx/gl/gfxGLDevice.h"
+
+#include "core/frameAllocator.h"
+#include "core/stream/fileStream.h"
+#include "core/strings/stringFunctions.h"
+#include "math/mPoint2.h"
+#include "gfx/gfxStructs.h"
+#include "console/console.h"
+
+#define CHECK_AARG(pos, name) static StringTableEntry attr_##name = StringTable->insert(#name); if (argName == attr_##name) { glBindAttribLocation(mProgram, pos, attr_##name); continue; }
+
+class GFXGLShaderConstHandle : public GFXShaderConstHandle
+{
+   friend class GFXGLShader;
+
+public:  
+   
+   GFXGLShaderConstHandle( GFXGLShader *shader );
+   GFXGLShaderConstHandle( GFXGLShader *shader, const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum );
+   virtual ~GFXGLShaderConstHandle();
+   
+   void reinit( const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum );
+
+   const String& getName() const { return mDesc.name; }
+   GFXShaderConstType getType() const { return mDesc.constType; }
+   U32 getArraySize() const { return mDesc.arraySize; }
+
+   U32 getSize() const;
+   void setValid( bool valid ) { mValid = valid; }   
+   /// @warning This will always return the value assigned when the shader was
+   /// initialized.  If the value is later changed this method won't reflect that.
+   S32 getSamplerRegister() const { return mSamplerNum; }
+
+   GFXShaderConstDesc mDesc;
+   GFXGLShader* mShader;
+   GLuint mLocation;
+   U32 mOffset;
+   U32 mSize;  
+   S32 mSamplerNum; 
+   bool mInstancingConstant;
+};
+
+GFXGLShaderConstHandle::GFXGLShaderConstHandle( GFXGLShader *shader )
+ : mShader( shader ), mLocation(0), mOffset(0), mSize(0), mSamplerNum(-1), mInstancingConstant(false)
+{
+   mValid = false;
+}
+
+static U32 shaderConstTypeSize(GFXShaderConstType type)
+{
+   switch(type) 
+   {
+   case GFXSCT_Float:
+   case GFXSCT_Int:
+   case GFXSCT_Sampler:
+   case GFXSCT_SamplerCube:
+   case GFXSCT_SamplerCubeArray:
+      return 4;
+   case GFXSCT_Float2:
+   case GFXSCT_Int2:
+      return 8;
+   case GFXSCT_Float3:
+   case GFXSCT_Int3:
+      return 12;
+   case GFXSCT_Float4:
+   case GFXSCT_Int4:
+      return 16;
+   case GFXSCT_Float2x2:
+      return 16;
+   case GFXSCT_Float3x3:
+      return 36;
+   case GFXSCT_Float4x3:
+      return 48;
+   case GFXSCT_Float4x4:
+      return 64;
+   default:
+      AssertFatal(false,"shaderConstTypeSize - Unrecognized constant type");
+      return 0;
+   }
+}
+
+GFXGLShaderConstHandle::GFXGLShaderConstHandle( GFXGLShader *shader, const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ) 
+ : mShader(shader), mInstancingConstant(false)
+{
+   reinit(desc, loc, samplerNum);
+}
+
+void GFXGLShaderConstHandle::reinit( const GFXShaderConstDesc& desc, GLuint loc, S32 samplerNum )
+{
+   mDesc = desc;
+   mLocation = loc;
+   mSamplerNum = samplerNum;
+   mOffset = 0;
+   mInstancingConstant = false;
+   
+   U32 elemSize = shaderConstTypeSize(mDesc.constType);
+   AssertFatal(elemSize, "GFXGLShaderConst::GFXGLShaderConst - elemSize is 0");
+   mSize = mDesc.arraySize * elemSize;
+   mValid = true;
+}
+
+
+U32 GFXGLShaderConstHandle::getSize() const
+{
+   return mSize;
+}
+
+GFXGLShaderConstHandle::~GFXGLShaderConstHandle()
+{
+}
+
+GFXGLShaderConstBuffer::GFXGLShaderConstBuffer(GFXGLShader* shader, U32 bufSize, U8* existingConstants)
+{
+   mShader = shader;
+   mBuffer = new U8[bufSize];
+   mWasLost = true;
+
+   // Copy the existing constant buffer to preserve sampler numbers
+   /// @warning This preserves a lot more than sampler numbers, obviously. If there
+   /// is any code that assumes a new constant buffer will have everything set to
+   /// 0, it will break.
+   dMemcpy(mBuffer, existingConstants, bufSize);
+}
+
+GFXGLShaderConstBuffer::~GFXGLShaderConstBuffer()
+{
+   delete[] mBuffer;
+
+   if ( mShader )
+      mShader->_unlinkBuffer( this );
+}
+
+template<typename ConstType>
+void GFXGLShaderConstBuffer::internalSet(GFXShaderConstHandle* handle, const ConstType& param)
+{
+   AssertFatal(handle, "GFXGLShaderConstBuffer::internalSet - Handle is NULL!" );
+   AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::internalSet - Handle is not valid!" );
+   AssertFatal(dynamic_cast<GFXGLShaderConstHandle*>(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type");
+
+   GFXGLShaderConstHandle* _glHandle = static_cast<GFXGLShaderConstHandle*>(handle);
+   AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader");
+   U8 *buf = mBuffer + _glHandle->mOffset;
+
+   if(_glHandle->mInstancingConstant)            
+      buf = mInstPtr + _glHandle->mOffset;
+
+   dMemcpy(buf, &param, sizeof(ConstType));
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const F32 fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2F& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3F& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4F& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const PlaneF& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const LinearColorF& fv)
+{
+   internalSet(handle, fv);
+}
+ 
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const S32 fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2I& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3I& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4I& fv)
+{
+   internalSet(handle, fv);
+}
+
+template<typename ConstType>
+void GFXGLShaderConstBuffer::internalSet(GFXShaderConstHandle* handle, const AlignedArray<ConstType>& fv)
+{
+   AssertFatal(handle, "GFXGLShaderConstBuffer::internalSet - Handle is NULL!" );
+   AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::internalSet - Handle is not valid!" );
+   AssertFatal(dynamic_cast<GFXGLShaderConstHandle*>(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type");
+
+   GFXGLShaderConstHandle* _glHandle = static_cast<GFXGLShaderConstHandle*>(handle);
+   AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader");
+   AssertFatal(!_glHandle->mInstancingConstant, "GFXGLShaderConstBuffer::set - Instancing not supported for array");
+   const U8* fvBuffer = static_cast<const U8*>(fv.getBuffer());
+   for(U32 i = 0; i < fv.size(); ++i)
+   {
+      dMemcpy(mBuffer + _glHandle->mOffset + i * sizeof(ConstType), fvBuffer, sizeof(ConstType));
+      fvBuffer += fv.getElementSize();
+   }
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<F32>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<Point2F>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<Point3F>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<Point4F>& fv)   
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<S32>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<Point2I>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<Point3I>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray<Point4I>& fv)
+{
+   internalSet(handle, fv);
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matType)
+{
+   AssertFatal(handle, "GFXGLShaderConstBuffer::set - Handle is NULL!" );
+   AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::set - Handle is not valid!" );
+   AssertFatal(dynamic_cast<GFXGLShaderConstHandle*>(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type");
+
+   GFXGLShaderConstHandle* _glHandle = static_cast<GFXGLShaderConstHandle*>(handle);
+   AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader");
+   AssertFatal(!_glHandle->mInstancingConstant || matType == GFXSCT_Float4x4, "GFXGLShaderConstBuffer::set - Only support GFXSCT_Float4x4 for instancing");
+   
+   switch(matType)
+   {
+   case GFXSCT_Float2x2:
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[0] = mat[0];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[1] = mat[1];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[2] = mat[4];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[3] = mat[5];
+      break;
+   case GFXSCT_Float3x3:
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[0] = mat[0];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[1] = mat[1];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[2] = mat[2];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[3] = mat[4];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[4] = mat[5];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[5] = mat[6];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[6] = mat[8];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[7] = mat[9];
+      reinterpret_cast<F32*>(mBuffer + _glHandle->mOffset)[8] = mat[10];
+      break;
+   case GFXSCT_Float4x3:
+      dMemcpy(mBuffer + _glHandle->mOffset, (const F32*)mat, (sizeof(F32) * 12));// matrix with end row chopped off
+      break;
+   case GFXSCT_Float4x4:
+   {      
+      if(_glHandle->mInstancingConstant)
+      {
+         MatrixF transposed;   
+         mat.transposeTo(transposed);
+         dMemcpy( mInstPtr + _glHandle->mOffset, (const F32*)transposed, sizeof(MatrixF) );
+         return;
+      }
+      
+      dMemcpy(mBuffer + _glHandle->mOffset, (const F32*)mat, sizeof(MatrixF));
+      break;
+   }
+   default:
+      AssertFatal(false, "GFXGLShaderConstBuffer::set - Invalid matrix type");
+      break;
+   }
+}
+
+void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType)
+{
+   AssertFatal(handle, "GFXGLShaderConstBuffer::set - Handle is NULL!" );
+   AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::set - Handle is not valid!" );
+
+   GFXGLShaderConstHandle* _glHandle = static_cast<GFXGLShaderConstHandle*>(handle);
+   AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader");  
+   AssertFatal(!_glHandle->mInstancingConstant, "GFXGLShaderConstBuffer::set - Instancing not supported for matrix arrays");
+
+   switch (matrixType) {
+      case GFXSCT_Float4x3:
+         // Copy each item with the last row chopped off
+         for (int i = 0; i<arraySize; i++)
+         {
+            dMemcpy(mBuffer + _glHandle->mOffset + (i*(sizeof(F32) * 12)), (F32*)(mat + i), sizeof(F32) * 12);
+         }
+      break;
+      case GFXSCT_Float4x4:
+         dMemcpy(mBuffer + _glHandle->mOffset, (F32*)mat, _glHandle->getSize());
+         break;
+      default:
+         AssertFatal(false, "GFXGLShaderConstBuffer::set - setting array of non 4x4 matrices!");
+         break;
+   }
+}
+
+void GFXGLShaderConstBuffer::activate()
+{
+   PROFILE_SCOPE(GFXGLShaderConstBuffer_activate);
+   mShader->setConstantsFromBuffer(this);
+   mWasLost = false;
+}
+
+const String GFXGLShaderConstBuffer::describeSelf() const
+{
+   return String();
+}
+
+void GFXGLShaderConstBuffer::onShaderReload( GFXGLShader *shader )
+{
+   AssertFatal( shader == mShader, "GFXGLShaderConstBuffer::onShaderReload, mismatched shaders!" );
+
+   delete[] mBuffer;
+   mBuffer = new U8[mShader->mConstBufferSize];
+   dMemset(mBuffer, 0, mShader->mConstBufferSize);
+   mWasLost = true;
+}
+
+GFXGLShader::GFXGLShader() :
+   mVertexShader(0),
+   mPixelShader(0),
+   mProgram(0),
+   mConstBufferSize(0),
+   mConstBuffer(NULL)
+{
+}
+
+GFXGLShader::~GFXGLShader()
+{
+   clearShaders();
+   for(HandleMap::Iterator i = mHandles.begin(); i != mHandles.end(); i++)
+      delete i->value;
+   
+   delete[] mConstBuffer;
+}
+
+void GFXGLShader::clearShaders()
+{
+   glDeleteProgram(mProgram);
+   glDeleteShader(mVertexShader);
+   glDeleteShader(mPixelShader);
+   
+   mProgram = 0;
+   mVertexShader = 0;
+   mPixelShader = 0;
+}
+
+bool GFXGLShader::_init()
+{
+   PROFILE_SCOPE(GFXGLShader_Init);
+   // Don't initialize empty shaders.
+   if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() )
+      return false;
+   
+   clearShaders();
+   
+   mProgram = glCreateProgram();
+   
+   // Set the macros and add the global ones.
+   Vector<GFXShaderMacro> macros;
+   macros.merge( mMacros );
+   macros.merge( smGlobalMacros );
+   
+   macros.increment();
+   macros.last().name = "TORQUE_SM";
+   macros.last().value = 40;
+   macros.increment();
+   macros.last().name = "TORQUE_VERTEX_SHADER";
+   macros.last().value = "";
+   
+   // Default to true so we're "successful" if a vertex/pixel shader wasn't specified.
+   bool compiledVertexShader = true;
+   bool compiledPixelShader = true;
+   
+   // Compile the vertex and pixel shaders if specified.
+   if(!mVertexFile.isEmpty())
+      compiledVertexShader = initShader(mVertexFile, true, macros);
+   
+   macros.last().name = "TORQUE_PIXEL_SHADER";
+   if(!mPixelFile.isEmpty())
+      compiledPixelShader = initShader(mPixelFile, false, macros);
+   
+   // If either shader was present and failed to compile, bail.
+   if(!compiledVertexShader || !compiledPixelShader)
+      return false;
+   
+   // Link it!
+   glLinkProgram( mProgram );
+   
+   GLint activeAttribs  = 0;
+   glGetProgramiv(mProgram, GL_ACTIVE_ATTRIBUTES, &activeAttribs );
+   
+   GLint maxLength;
+   glGetProgramiv(mProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
+   
+   FrameTemp<GLchar> tempData(maxLength+1);
+   *tempData.address() = '\0';
+   // Check atributes
+   for (U32 i=0; i<activeAttribs; i++)
+   {
+      GLint size;
+      GLenum type;
+      
+      glGetActiveAttrib(mProgram, i, maxLength + 1, NULL, &size, &type, tempData.address());
+      
+      StringTableEntry argName = StringTable->insert(tempData.address());
+      
+      CHECK_AARG(Torque::GL_VertexAttrib_Position,    vPosition);
+      CHECK_AARG(Torque::GL_VertexAttrib_Normal,      vNormal);
+      CHECK_AARG(Torque::GL_VertexAttrib_Color,       vColor);
+      CHECK_AARG(Torque::GL_VertexAttrib_Tangent,     vTangent);
+      CHECK_AARG(Torque::GL_VertexAttrib_TangentW,    vTangentW);
+      CHECK_AARG(Torque::GL_VertexAttrib_Binormal,    vBinormal);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord0,   vTexCoord0);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord1,   vTexCoord1);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord2,   vTexCoord2);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord3,   vTexCoord3);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord4,   vTexCoord4);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord5,   vTexCoord5);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord6,   vTexCoord6);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord7,   vTexCoord7);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord8,   vTexCoord8);
+      CHECK_AARG(Torque::GL_VertexAttrib_TexCoord9,   vTexCoord9);
+   }
+   
+   //always have OUT_col
+   glBindFragDataLocation(mProgram, 0, "OUT_col");
+   // Check OUT_colN
+   for(U32 i=1;i<4;i++)
+   {
+      char buffer[10];
+      dSprintf(buffer, sizeof(buffer), "OUT_col%u",i);
+      GLint location = glGetFragDataLocation(mProgram, buffer);
+      if(location>0)
+         glBindFragDataLocation(mProgram, i, buffer);
+      
+   }
+   
+   // Link it again!
+   glLinkProgram( mProgram );
+   
+   GLint linkStatus;
+   glGetProgramiv( mProgram, GL_LINK_STATUS, &linkStatus );
+   
+   // Dump the info log to the console
+   U32 logLength = 0;
+   glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, (GLint*)&logLength);
+   if ( logLength )
+   {
+      FrameAllocatorMarker fam;
+      char* log = (char*)fam.alloc( logLength );
+      glGetProgramInfoLog( mProgram, logLength, NULL, log );
+      
+      if ( linkStatus == GL_FALSE )
+      {
+         if ( smLogErrors )
+         {
+            Con::errorf( "GFXGLShader::init - Error linking shader!" );
+            Con::errorf( "Program %s / %s: %s",
+                        mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log);
+         }
+      }
+      else if ( smLogWarnings )
+      {
+         Con::warnf( "Program %s / %s: %s",
+                    mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log);
+      }
+   }
+   
+   
+   // If we failed to link, bail.
+   if ( linkStatus == GL_FALSE )
+      return false;
+   
+   initConstantDescs();
+   initHandles();
+   
+   // Notify Buffers we might have changed in size.
+   // If this was our first init then we won't have any activeBuffers
+   // to worry about unnecessarily calling.
+   Vector<GFXShaderConstBuffer*>::iterator biter = mActiveBuffers.begin();
+   for ( ; biter != mActiveBuffers.end(); biter++ )
+      ((GFXGLShaderConstBuffer*)(*biter))->onShaderReload( this );
+   
+   return true;
+}
+
+
+void GFXGLShader::initConstantDescs()
+{
+   mConstants.clear();
+   GLint numUniforms;
+   glGetProgramiv(mProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
+   GLint maxNameLength;
+   glGetProgramiv(mProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength);
+
+   if(!maxNameLength)
+      return;
+
+   FrameTemp<GLchar> uniformName(maxNameLength);
+   
+   for(U32 i = 0; i < numUniforms; i++)
+   {
+      GLint size;
+      GLenum type;
+      glGetActiveUniform(mProgram, i, maxNameLength, NULL, &size, &type, uniformName);
+      GFXShaderConstDesc desc;
+      
+      desc.name = String((char*)uniformName);
+      
+      // Remove array brackets from the name
+      desc.name = desc.name.substr(0, desc.name.find('['));
+      
+      // Insert $ to match D3D behavior of having a $ prepended to parameters to main.
+      desc.name.insert(0, '$');
+      desc.arraySize = size;
+      
+      switch(type)
+      {
+         case GL_FLOAT:
+            desc.constType = GFXSCT_Float;
+            break;
+         case GL_FLOAT_VEC2:
+            desc.constType = GFXSCT_Float2;
+            break;
+         case GL_FLOAT_VEC3:
+            desc.constType = GFXSCT_Float3;
+            break;
+         case GL_FLOAT_VEC4:
+            desc.constType = GFXSCT_Float4;
+            break;
+         case GL_INT:
+            desc.constType = GFXSCT_Int;
+            break;
+         case GL_INT_VEC2:
+            desc.constType = GFXSCT_Int2;
+            break;
+         case GL_INT_VEC3:
+            desc.constType = GFXSCT_Int3;
+            break;
+         case GL_INT_VEC4:
+            desc.constType = GFXSCT_Int4;
+            break;
+         case GL_FLOAT_MAT2:
+            desc.constType = GFXSCT_Float2x2;
+            break;
+         case GL_FLOAT_MAT3:
+            desc.constType = GFXSCT_Float3x3;
+            break;
+         case GL_FLOAT_MAT4:
+            desc.constType = GFXSCT_Float4x4;
+            break;
+         case GL_FLOAT_MAT4x3: // jamesu - columns, rows
+            desc.constType = GFXSCT_Float4x3;
+            break;
+         case GL_SAMPLER_1D:
+         case GL_SAMPLER_2D:
+         case GL_SAMPLER_3D:
+         case GL_SAMPLER_1D_SHADOW:
+         case GL_SAMPLER_2D_SHADOW:
+            desc.constType = GFXSCT_Sampler;
+            break;
+         case GL_SAMPLER_CUBE:
+            desc.constType = GFXSCT_SamplerCube;
+            break;
+         case GL_SAMPLER_CUBE_MAP_ARRAY:
+            desc.constType = GFXSCT_SamplerCubeArray;
+            break;
+         default:
+            AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type");
+            // If we don't recognize the constant don't add its description.
+            continue;
+      }
+      
+      mConstants.push_back(desc);
+   }
+}
+
+void GFXGLShader::initHandles()
+{      
+   // Mark all existing handles as invalid.
+   // Those that are found when parsing the descriptions will then be marked valid again.
+   for ( HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter )      
+      (iter->value)->setValid( false );  
+   mValidHandles.clear();
+
+   // Loop through all ConstantDescriptions, 
+   // if they aren't in the HandleMap add them, if they are reinitialize them.
+   for ( U32 i = 0; i < mConstants.size(); i++ )
+   {
+      GFXShaderConstDesc &desc = mConstants[i];            
+
+      // Index element 1 of the name to skip the '$' we inserted earier.
+      GLint loc = glGetUniformLocation(mProgram, &desc.name.c_str()[1]);
+
+      AssertFatal(loc != -1, "");
+
+      HandleMap::Iterator handle = mHandles.find(desc.name);
+      S32 sampler = -1;
+      if(desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube || desc.constType == GFXSCT_SamplerCubeArray)
+      {
+         S32 idx = mSamplerNamesOrdered.find_next(desc.name);
+         AssertFatal(idx != -1, "");
+         sampler = idx; //assignedSamplerNum++;
+      }
+      if ( handle != mHandles.end() )
+      {
+         handle->value->reinit( desc, loc, sampler );         
+      } 
+      else 
+      {
+         mHandles[desc.name] = new GFXGLShaderConstHandle( this, desc, loc, sampler );      
+      }
+   }
+
+   // Loop through handles once more to set their offset and calculate our
+   // constBuffer size.
+
+   if ( mConstBuffer )
+      delete[] mConstBuffer;
+   mConstBufferSize = 0;
+
+   for ( HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter )
+   {
+      GFXGLShaderConstHandle* handle = iter->value;
+      if ( handle->isValid() )
+      {
+      	mValidHandles.push_back(handle);
+         handle->mOffset = mConstBufferSize;
+         mConstBufferSize += handle->getSize();
+      }
+   }
+   
+   mConstBuffer = new U8[mConstBufferSize];
+   dMemset(mConstBuffer, 0, mConstBufferSize);
+   
+   // Set our program so uniforms are assigned properly.
+   glUseProgram(mProgram);
+   // Iterate through uniforms to set sampler numbers.
+   for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter)
+   {
+      GFXGLShaderConstHandle* handle = iter->value;
+      if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube || handle->getType() == GFXSCT_SamplerCubeArray))
+      {
+         // Set sampler number on our program.
+         glUniform1i(handle->mLocation, handle->mSamplerNum);
+         // Set sampler in constant buffer so it does not get unset later.
+         dMemcpy(mConstBuffer + handle->mOffset, &handle->mSamplerNum, handle->getSize());
+      }
+   }
+   glUseProgram(0);
+
+   //instancing
+   if (!mInstancingFormat)
+      return;
+
+   U32 offset = 0;
+
+   for ( U32 i=0; i < mInstancingFormat->getElementCount(); i++ )
+   {
+      const GFXVertexElement &element = mInstancingFormat->getElement( i );
+      
+      String constName = String::ToString( "$%s", element.getSemantic().c_str() );
+
+      HandleMap::Iterator handle = mHandles.find(constName);      
+      if ( handle != mHandles.end() )
+      {          
+         AssertFatal(0, "");
+      } 
+      else 
+      {
+         GFXShaderConstDesc desc;
+         desc.name = constName;
+         desc.arraySize = 1;
+         switch(element.getType())
+         {
+         case GFXDeclType_Float4:
+            desc.constType = GFXSCT_Float4;
+            break;
+
+         default:
+            desc.constType = GFXSCT_Float;
+            break;
+         }
+         
+         GFXGLShaderConstHandle *h = new GFXGLShaderConstHandle( this, desc, -1, -1 );
+         h->mInstancingConstant = true;
+         h->mOffset = offset;
+         mHandles[constName] =  h;
+
+         offset += element.getSizeInBytes();
+         ++i;
+
+         // If this is a matrix we will have 2 or 3 more of these
+         // semantics with the same name after it.
+         for ( ; i < mInstancingFormat->getElementCount(); i++ )
+         {
+            const GFXVertexElement &nextElement = mInstancingFormat->getElement( i );
+            if ( nextElement.getSemantic() != element.getSemantic() )
+            {
+               i--;
+               break;
+            }
+            ++desc.arraySize;
+            if(desc.arraySize == 4 && desc.constType == GFXSCT_Float4)
+            {
+               desc.arraySize = 1;
+               desc.constType = GFXSCT_Float4x4;
+            }
+            offset += nextElement.getSizeInBytes();
+         }
+      }
+
+   }
+}
+
+GFXShaderConstHandle* GFXGLShader::getShaderConstHandle(const String& name)
+{
+   HandleMap::Iterator i = mHandles.find(name);
+   if(i != mHandles.end())
+      return i->value;
+   else
+   {
+      GFXGLShaderConstHandle* handle = new GFXGLShaderConstHandle( this );
+      mHandles[ name ] = handle;
+      
+      return handle;
+   }
+}
+
+GFXShaderConstHandle* GFXGLShader::findShaderConstHandle(const String& name)
+{
+   HandleMap::Iterator i = mHandles.find(name);
+   if(i != mHandles.end())
+      return i->value;
+   else
+   {
+      return NULL;
+   }
+}
+
+void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer)
+{
+   for(Vector<GFXGLShaderConstHandle*>::iterator i = mValidHandles.begin(); i != mValidHandles.end(); ++i)
+   {
+      GFXGLShaderConstHandle* handle = *i;
+      AssertFatal(handle, "GFXGLShader::setConstantsFromBuffer - Null handle");
+
+      if(handle->mInstancingConstant)
+         continue;
+      
+      // Don't set if the value has not be changed.
+      if(dMemcmp(mConstBuffer + handle->mOffset, buffer->mBuffer + handle->mOffset, handle->getSize()) == 0)
+         continue;
+         
+      // Copy new value into our const buffer and set in GL.
+      dMemcpy(mConstBuffer + handle->mOffset, buffer->mBuffer + handle->mOffset, handle->getSize());
+      switch(handle->mDesc.constType)
+      {
+         case GFXSCT_Float:
+            glUniform1fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float2:
+            glUniform2fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float3:
+            glUniform3fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float4:
+            glUniform4fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Int:
+         case GFXSCT_Sampler:
+         case GFXSCT_SamplerCube:
+         case GFXSCT_SamplerCubeArray:
+            glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Int2:
+            glUniform2iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Int3:
+            glUniform3iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Int4:
+            glUniform4iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float2x2:
+            glUniformMatrix2fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float3x3:
+            glUniformMatrix3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float4x3:
+            // NOTE: To save a transpose here we could store the matrix transposed (i.e. column major) in the constant buffer.
+            // See _mesa_uniform_matrix in the mesa source for the correct transpose algorithm for a 4x3 matrix. 
+            glUniformMatrix4x3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         case GFXSCT_Float4x4:
+            glUniformMatrix4fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset));
+            break;
+         default:
+            AssertFatal(0,"");
+            break;
+      }
+   }
+}
+
+GFXShaderConstBufferRef GFXGLShader::allocConstBuffer()
+{
+   GFXGLShaderConstBuffer* buffer = new GFXGLShaderConstBuffer(this, mConstBufferSize, mConstBuffer);
+   buffer->registerResourceWithDevice(getOwningDevice());
+   mActiveBuffers.push_back( buffer );
+   return buffer;
+}
+
+void GFXGLShader::useProgram()
+{
+   glUseProgram(mProgram);
+}
+
+void GFXGLShader::zombify()
+{
+   clearShaders();
+   dMemset(mConstBuffer, 0, mConstBufferSize);
+}
+
+char* GFXGLShader::_handleIncludes( const Torque::Path& path, FileStream *s )
+{
+   // TODO:  The #line pragma on GLSL takes something called a
+   // "source-string-number" which it then never explains.
+   //
+   // Until i resolve this mystery i disabled this.
+   //
+   //String linePragma = String::ToString( "#line 1 \r\n");
+   //U32 linePragmaLen = linePragma.length();
+
+   U32 shaderLen = s->getStreamSize();
+   char* buffer = (char*)dMalloc(shaderLen + 1);
+   //dStrncpy( buffer, linePragma.c_str(), linePragmaLen );
+   s->read(shaderLen, buffer);
+   buffer[shaderLen] = 0;
+   
+   char* p = dStrstr(buffer, "#include");
+   while(p)
+   {
+      char* q = p;
+      p += 8;
+      if(dIsspace(*p))
+      {
+         U32 n = 0;
+         while(dIsspace(*p)) ++p;
+         AssertFatal(*p == '"', "Bad #include directive");
+         ++p;
+         static char includeFile[256];
+         while(*p != '"')
+         {
+            AssertFatal(*p != 0, "Bad #include directive");
+            includeFile[n++] = *p++;
+            AssertFatal(n < sizeof(includeFile), "#include directive too long");
+         }
+         ++p;
+         includeFile[n] = 0;
+
+         // First try it as a local file.
+         Torque::Path includePath = Torque::Path::Join(path.getPath(), '/', includeFile);
+         includePath = Torque::Path::CompressPath(includePath);
+         
+         FileStream includeStream;
+
+         if ( !includeStream.open( includePath, Torque::FS::File::Read ) )
+         {
+            // Try again assuming the path is absolute 
+            // and/or relative.
+            includePath = String( includeFile );
+            includePath = Torque::Path::CompressPath(includePath);
+            if ( !includeStream.open( includePath, Torque::FS::File::Read ) )
+            {
+               AssertISV(false, avar("failed to open include '%s'.", includePath.getFullPath().c_str()));
+
+               if ( smLogErrors )
+                  Con::errorf( "GFXGLShader::_handleIncludes - Failed to open include '%s'.", 
+                     includePath.getFullPath().c_str() );
+
+               // Fail... don't return the buffer.
+               dFree(buffer);
+               return NULL;
+            }
+         }
+
+         char* includedText = _handleIncludes(includePath, &includeStream);
+         
+         // If a sub-include fails... cleanup and return.
+         if ( !includedText )
+         {
+            dFree(buffer);
+            return NULL;
+         }
+         
+         // TODO: Disabled till this is fixed correctly.
+         //
+         // Count the number of lines in the file 
+         // before the include.
+         /*
+         U32 includeLine = 0;
+         {
+            char* nl = dStrstr( buffer, "\n" );
+            while ( nl )
+            {
+               includeLine++;
+               nl = dStrstr( nl, "\n" );
+               if(nl) ++nl;
+            }
+         }
+         */
+
+         String manip(buffer);
+         manip.erase(q-buffer, p-q);
+         String sItx(includedText);
+
+         // TODO: Disabled till this is fixed correctly.
+         //
+         // Add a new line pragma to restore the proper
+         // file and line number after the include.
+         //sItx += String::ToString( "\r\n#line %d \r\n", includeLine );
+         
+         dFree(includedText);
+         manip.insert(q-buffer, sItx);
+         char* manipBuf = dStrdup(manip.c_str());
+         p = manipBuf + (q - buffer);
+         dFree(buffer);
+         buffer = manipBuf;
+      }
+      p = dStrstr(p, "#include");
+   }
+   
+   return buffer;
+}
+
+bool GFXGLShader::_loadShaderFromStream(  GLuint shader, 
+                                          const Torque::Path &path, 
+                                          FileStream *s, 
+                                          const Vector<GFXShaderMacro> &macros )
+{
+   Vector<char*> buffers;
+   Vector<U32> lengths;
+   
+   // The GLSL version declaration must go first!
+   const char *versionDecl = "#version 150\r\n";
+   buffers.push_back( dStrdup( versionDecl ) );
+   lengths.push_back( dStrlen( versionDecl ) );
+
+   if(GFXGL->mCapabilities.shaderModel5)
+   {
+      const char *extension = "#extension GL_ARB_gpu_shader5 : enable\r\n";
+      buffers.push_back( dStrdup( extension ) );
+      lengths.push_back( dStrlen( extension ) );
+   }
+
+   const char *newLine = "\r\n";
+   buffers.push_back( dStrdup( newLine ) );
+   lengths.push_back( dStrlen( newLine ) );
+   
+   // Now add all the macros.
+   for( U32 i = 0; i < macros.size(); i++ )
+   {
+      if(macros[i].name.isEmpty())  // TODO OPENGL
+         continue;
+
+      String define = String::ToString( "#define %s %s\n", macros[i].name.c_str(), macros[i].value.c_str() );
+      buffers.push_back( dStrdup( define.c_str() ) );
+      lengths.push_back( define.length() );
+   }
+   
+   // Now finally add the shader source.
+   U32 shaderLen = s->getStreamSize();
+   char *buffer = _handleIncludes(path, s);
+   if ( !buffer )
+      return false;
+   
+   buffers.push_back(buffer);
+   lengths.push_back(shaderLen);
+   
+   glShaderSource(shader, buffers.size(), (const GLchar**)const_cast<const char**>(buffers.address()), NULL);
+
+#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX)
+   FileStream stream;
+   if ( !stream.open( path.getFullPath()+"_DEBUG", Torque::FS::File::Write ) )
+   {
+      AssertISV(false, avar("GFXGLShader::initShader - failed to write debug shader '%s'.", path.getFullPath().c_str()));
+   }
+
+   for(int i = 0; i < buffers.size(); ++i)
+         stream.writeText(buffers[i]);
+#endif
+
+   // Cleanup the shader source buffer.
+   for ( U32 i=0; i < buffers.size(); i++ )
+      dFree( buffers[i] );
+
+   glCompileShader(shader);
+
+   return true;
+}
+
+bool GFXGLShader::initShader( const Torque::Path &file, 
+                              bool isVertex, 
+                              const Vector<GFXShaderMacro> &macros )
+{
+   PROFILE_SCOPE(GFXGLShader_CompileShader);
+   GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
+   if(isVertex)
+      mVertexShader = activeShader;
+   else
+      mPixelShader = activeShader;
+   glAttachShader(mProgram, activeShader);
+   
+   
+   // Ok it's not in the shader gen manager, so ask Torque for it
+   FileStream stream;
+   if ( !stream.open( file, Torque::FS::File::Read ) )
+   {
+      AssertISV(false, avar("GFXGLShader::initShader - failed to open shader '%s'.", file.getFullPath().c_str()));
+
+      if ( smLogErrors )
+         Con::errorf( "GFXGLShader::initShader - Failed to open shader file '%s'.", 
+            file.getFullPath().c_str() );
+
+      return false;
+   }
+   
+   if ( !_loadShaderFromStream( activeShader, file, &stream, macros ) )
+      return false;
+   
+   GLint compile;
+   glGetShaderiv(activeShader, GL_COMPILE_STATUS, &compile);
+
+   // Dump the info log to the console
+   U32 logLength = 0;
+   glGetShaderiv(activeShader, GL_INFO_LOG_LENGTH, (GLint*)&logLength);
+   
+   GLint compileStatus = GL_TRUE;
+   if ( logLength )
+   {
+      FrameAllocatorMarker fam;
+      char* log = (char*)fam.alloc(logLength);
+      glGetShaderInfoLog(activeShader, logLength, NULL, log);
+
+      // Always print errors
+      glGetShaderiv( activeShader, GL_COMPILE_STATUS, &compileStatus );
+
+      if ( compileStatus == GL_FALSE )
+      {
+         if ( smLogErrors )
+         {
+            Con::errorf( "GFXGLShader::initShader - Error compiling shader!" );
+            Con::errorf( "Program %s: %s", file.getFullPath().c_str(), log );
+         }
+      }
+      else if ( smLogWarnings )
+         Con::warnf( "Program %s: %s", file.getFullPath().c_str(), log );
+   }
+
+   return compileStatus != GL_FALSE;
+}
+
+/// Returns our list of shader constants, the material can get this and just set the constants it knows about
+const Vector<GFXShaderConstDesc>& GFXGLShader::getShaderConstDesc() const
+{
+   PROFILE_SCOPE(GFXGLShader_GetShaderConstants);
+   return mConstants;
+}
+
+/// Returns the alignment value for constType
+U32 GFXGLShader::getAlignmentValue(const GFXShaderConstType constType) const
+{
+   // Alignment is the same thing as size for us.
+   return shaderConstTypeSize(constType);
+}
+
+const String GFXGLShader::describeSelf() const
+{
+   String ret;
+   ret = String::ToString("   Program: %i", mProgram);
+   ret += String::ToString("   Vertex Path: %s", mVertexFile.getFullPath().c_str());
+   ret += String::ToString("   Pixel Path: %s", mPixelFile.getFullPath().c_str());
+   
+   return ret;
+}

+ 141 - 0
gl/gfxGLStateCache.h

@@ -0,0 +1,141 @@
+#ifndef GFX_GL_STATE_CACHE
+#define GFX_GL_STATE_CACHE
+
+
+/// GFXGLStateCache store OpenGL state to avoid performance penalities of glGet* calls
+/// GL_TEXTURE_1D/2D/3D, GL_FRAMEBUFFER, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER
+class GFXGLStateCache
+{
+public:
+   GFXGLStateCache()
+   {      
+      mActiveTexture = 0;      
+      mBindedVBO = 0;
+      mBindedIBO = 0;
+      mBindedFBO_W = 0;
+      mBindedFBO_R = 0;
+      mVertexAttribActive = 0;
+   }
+
+   class TextureUnit
+   {
+   public:
+      TextureUnit() :  mTexture1D(0), mTexture2D(0), mTexture3D(0), mTextureCube(0), mTextureCubeArray(0)
+      {
+
+      }
+      GLuint mTexture1D, mTexture2D, mTexture3D, mTextureCube, mTextureCubeArray;
+   };
+
+   /// after glBindTexture
+   void setCacheBindedTex(U32 texUnit, GLenum biding, GLuint handle)
+   { 
+      mActiveTexture = texUnit;
+      switch (biding)
+      {
+      case GL_TEXTURE_2D:
+         mTextureUnits[mActiveTexture].mTexture2D = handle;
+         break;
+      case GL_TEXTURE_3D:
+         mTextureUnits[mActiveTexture].mTexture3D = handle;
+         break;
+      case GL_TEXTURE_1D:
+         mTextureUnits[mActiveTexture].mTexture1D = handle;
+         break;
+      case GL_TEXTURE_CUBE_MAP:
+         mTextureUnits[mActiveTexture].mTextureCube = handle;
+         break;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         mTextureUnits[mActiveTexture].mTextureCubeArray = handle;
+         break;
+      default:
+         AssertFatal(0, avar("GFXGLStateCache::setCacheBindedTex - binding (%x) not supported.", biding) );
+         return;
+      }
+   }
+
+   /// after opengl object binded
+   void setCacheBinded(GLenum biding, GLuint handle) 
+   { 
+      switch (biding)
+      {
+      case GL_TEXTURE_2D:
+         mTextureUnits[mActiveTexture].mTexture2D = handle;
+         break;
+      case GL_TEXTURE_3D:
+         mTextureUnits[mActiveTexture].mTexture3D = handle;
+         break;
+      case GL_TEXTURE_1D:
+         mTextureUnits[mActiveTexture].mTexture1D = handle;
+         break;
+      case GL_TEXTURE_CUBE_MAP:
+         mTextureUnits[mActiveTexture].mTextureCube = handle;
+         break;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         mTextureUnits[mActiveTexture].mTextureCubeArray = handle;
+         break;
+      case GL_FRAMEBUFFER:
+         mBindedFBO_W = mBindedFBO_R = handle;
+         break;
+      case GL_DRAW_FRAMEBUFFER:
+         mBindedFBO_W = handle;
+         break;
+      case GL_READ_FRAMEBUFFER:
+         mBindedFBO_R = handle;
+         break;
+      case GL_ARRAY_BUFFER:
+         mBindedVBO = handle;
+         break;
+      case GL_ELEMENT_ARRAY_BUFFER:
+         mBindedIBO = handle;
+         break;
+      default:
+         AssertFatal(0, avar("GFXGLStateCache::setCacheBinded - binding (%x) not supported.", biding) );
+         break;
+      }
+   }
+
+   GLuint getCacheBinded(GLenum biding) const
+   {
+      switch (biding)
+      {
+      case GL_TEXTURE_2D:
+         return mTextureUnits[mActiveTexture].mTexture2D;
+      case GL_TEXTURE_3D:
+         return mTextureUnits[mActiveTexture].mTexture3D;
+      case GL_TEXTURE_1D:
+         return mTextureUnits[mActiveTexture].mTexture1D;
+      case GL_TEXTURE_CUBE_MAP:
+         return mTextureUnits[mActiveTexture].mTextureCube;
+      case GL_TEXTURE_CUBE_MAP_ARRAY:
+         return mTextureUnits[mActiveTexture].mTextureCubeArray;
+      case GL_DRAW_FRAMEBUFFER:
+         return mBindedFBO_W;
+      case GL_READ_FRAMEBUFFER:
+         return mBindedFBO_R;
+      case GL_ARRAY_BUFFER:
+         return mBindedVBO;
+      case GL_ELEMENT_ARRAY_BUFFER:
+         return mBindedIBO;
+      default:
+         AssertFatal(0, avar("GFXGLStateCache::getCacheBinded - binding (%x) not supported.", biding) );
+         return 0;
+      }
+   }
+
+   /// after glActiveTexture
+   void setCacheActiveTexture(U32 unit) { mActiveTexture = unit; }
+   U32 getCacheActiveTexture() const { return mActiveTexture;  }
+
+   /// for cache glEnableVertexAttribArray / glDisableVertexAttribArray
+   void setCacheVertexAttribActive(U32 activeMask) { mVertexAttribActive = activeMask; }
+   U32 getCacheVertexAttribActive() const { return mVertexAttribActive;  }
+
+protected:   
+   GLuint mActiveTexture, mBindedVBO, mBindedIBO, mBindedFBO_W, mBindedFBO_R;
+   TextureUnit mTextureUnits[TEXTURE_STAGE_COUNT];
+   U32 mVertexAttribActive;
+};
+
+
+#endif

+ 244 - 0
gl/gfxGLUtils.h

@@ -0,0 +1,244 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef TORQUE_GFX_GL_GFXGLUTILS_H_
+#define TORQUE_GFX_GL_GFXGLUTILS_H_
+
+#include "core/util/preprocessorHelpers.h"
+#include "gfx/gl/gfxGLEnumTranslate.h"
+#include "gfx/gl/gfxGLStateCache.h"
+#include "gfx/bitmap/imageUtils.h"
+
+inline U32 getMaxMipmaps(U32 width, U32 height, U32 depth)
+{
+   return getMax( getBinLog2(depth), getMax(getBinLog2(width), getBinLog2(height)));
+}
+
+inline GLenum minificationFilter(U32 minFilter, U32 mipFilter, U32 /*mipLevels*/)
+{
+   // the compiler should interpret this as array lookups
+   switch( minFilter ) 
+   {
+      case GFXTextureFilterLinear:
+         switch( mipFilter ) 
+         {
+         case GFXTextureFilterLinear:
+            return GL_LINEAR_MIPMAP_LINEAR;
+         case GFXTextureFilterPoint:
+            return GL_LINEAR_MIPMAP_NEAREST;
+         default: 
+            return GL_LINEAR;
+         }
+      default:
+         switch( mipFilter ) {
+      case GFXTextureFilterLinear:
+         return GL_NEAREST_MIPMAP_LINEAR;
+      case GFXTextureFilterPoint:
+         return GL_NEAREST_MIPMAP_NEAREST;
+      default:
+         return GL_NEAREST;
+         }
+   }
+}
+
+//Get the surface size of a compressed mip map level - see ddsLoader.cpp
+inline U32 getCompressedSurfaceSize(GFXFormat format,U32 width, U32 height, U32 mipLevel=0 )
+{
+   if(!ImageUtil::isCompressedFormat(format))
+      return 0;
+
+   // Bump by the mip level.
+   height = getMax(U32(1), height >> mipLevel);
+   width = getMax(U32(1), width >> mipLevel);
+
+   U32 sizeMultiple = 0;
+   if(format == GFXFormatBC1 || format == GFXFormatBC1_SRGB)
+      sizeMultiple = 8;
+   else
+      sizeMultiple = 16;
+
+   return getMax(U32(1), width/4) * getMax(U32(1), height/4) * sizeMultiple;
+}
+
+/// Simple class which preserves a given GL integer.
+/// This class determines the integer to preserve on construction and restores 
+/// it on destruction.
+class GFXGLPreserveInteger
+{
+public:
+   typedef void(STDCALL *BindFn)(GLenum, GLuint);
+
+   /// Preserve the integer.
+   /// @param binding The binding which should be set on destruction.
+   /// @param getBinding The parameter to be passed to glGetIntegerv to determine
+   /// the integer to be preserved.
+   /// @param binder The gl function to call to restore the integer.
+   GFXGLPreserveInteger(GLenum binding, GLint getBinding, BindFn binder) :
+      mBinding(binding), mPreserved(0), mBinder(binder)
+   {
+      AssertFatal(mBinder, "GFXGLPreserveInteger - Need a valid binder function");
+      mPreserved = GFXGL->getOpenglCache()->getCacheBinded(mBinding);
+#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX)
+      GLint bindedOnOpenglDriver;
+      glGetIntegerv(getBinding, &bindedOnOpenglDriver);
+      AssertFatal( mPreserved == bindedOnOpenglDriver, "GFXGLPreserveInteger - GFXGLDevice/OpenGL mismatch on cache binded resource.");
+#endif
+   }
+   
+   /// Restores the integer.
+   ~GFXGLPreserveInteger()
+   {
+      mBinder(mBinding, mPreserved);
+   }
+
+private:
+   GLenum mBinding;
+   GLint mPreserved;
+   BindFn mBinder;
+};
+
+class GFXGLPreserveTexture
+{
+public:
+   typedef void(STDCALL *BindFn)(GLenum, GLuint);
+   
+   GFXGLPreserveTexture(GLenum binding, GLint getBinding, BindFn binder) :
+      mBinding(binding), mPreserved(0), mBinder(binder)
+   {
+      AssertFatal(mBinder, "GFXGLPreserveTexture - Need a valid binder function");
+      GFXGLDevice *gfx = GFXGL;
+      mPreserved = gfx->getOpenglCache()->getCacheBinded(mBinding);
+      mActiveTexture = gfx->getOpenglCache()->getCacheActiveTexture();
+#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX)
+      GLint activeTextureOnOpenglDriver, bindedTextureOnOpenglDriver;
+      glGetIntegerv(getBinding, &bindedTextureOnOpenglDriver);
+      glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureOnOpenglDriver);
+      activeTextureOnOpenglDriver -= GL_TEXTURE0;
+      AssertFatal( mPreserved == bindedTextureOnOpenglDriver, "GFXGLPreserveTexture - GFXGLDevice/OpenGL mismatch on cache binded resource.");
+      AssertFatal( activeTextureOnOpenglDriver == mActiveTexture, "GFXGLPreserveTexture - GFXGLDevice/OpenGL mismatch on cache binded resource.");
+#endif
+   }
+   
+   /// Restores the texture.
+   ~GFXGLPreserveTexture()
+   {
+#if defined(TORQUE_DEBUG) && defined(TORQUE_DEBUG_GFX)
+      GLint activeTextureOnOpenglDriver;
+      glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTextureOnOpenglDriver);
+      activeTextureOnOpenglDriver -= GL_TEXTURE0;
+      GLint cacheActiveTexture = GFXGL->getOpenglCache()->getCacheActiveTexture();
+      AssertFatal( cacheActiveTexture == activeTextureOnOpenglDriver, "GFXGLPreserveTexture - GFXGLDevice/OpenGL mismatch on cache ActiveTexture.");
+#endif
+      mBinder(mBinding, mPreserved);
+   }
+
+private:
+   GLenum mBinding;
+   GLint mPreserved;
+   BindFn mBinder;
+   S16 mActiveTexture;
+};
+
+/// Helper macro to preserve the current VBO binding.
+#define PRESERVE_VERTEX_BUFFER() \
+GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindBuffer)
+
+/// Helper macro to preserve the current element array binding.
+#define PRESERVE_INDEX_BUFFER() \
+GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_ELEMENT_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindBuffer)
+
+#define _GET_BUFFER_BINDING( BINDING ) \
+BINDING == GL_ARRAY_BUFFER ? GL_ARRAY_BUFFER_BINDING : ( BINDING == GL_ELEMENT_ARRAY_BUFFER ?  GL_ELEMENT_ARRAY_BUFFER_BINDING : 0 )
+
+/// Helper macro to preserve the current element array binding.
+#define PRESERVE_BUFFER( BINDING ) \
+GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (BINDING, _GET_BUFFER_BINDING(BINDING), (GFXGLPreserveInteger::BindFn)glBindBuffer)
+
+/// ASSERT: Never call glActiveTexture for a "bind to modify" or in a PRESERVER_TEXTURE MACRO scope.
+
+/// Helper macro to preserve the current 1D texture binding.
+#define PRESERVE_1D_TEXTURE() \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_1D, GL_TEXTURE_BINDING_1D, (GFXGLPreserveInteger::BindFn)glBindTexture)
+
+/// Helper macro to preserve the current 2D texture binding.
+#define PRESERVE_2D_TEXTURE() \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D, (GFXGLPreserveInteger::BindFn)glBindTexture)
+
+/// Helper macro to preserve the current 3D texture binding.
+#define PRESERVE_3D_TEXTURE() \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D, (GFXGLPreserveInteger::BindFn)glBindTexture)
+
+/// Helper macro to preserve the current 3D texture binding.
+#define PRESERVE_CUBEMAP_TEXTURE() \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BINDING_CUBE_MAP, (GFXGLPreserveInteger::BindFn)glBindTexture)
+
+#define PRESERVE_CUBEMAP_ARRAY_TEXTURE() \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, (GFXGLPreserveInteger::BindFn)glBindTexture)
+
+#define _GET_TEXTURE_BINDING(binding) \
+binding == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : (binding == GL_TEXTURE_3D ?  GL_TEXTURE_BINDING_3D : GL_TEXTURE_BINDING_1D )
+
+#define PRESERVE_TEXTURE(binding) \
+GFXGLPreserveTexture TORQUE_CONCAT(preserve_, __LINE__) (binding, _GET_TEXTURE_BINDING(binding), (GFXGLPreserveInteger::BindFn)glBindTexture)
+
+#define PRESERVE_FRAMEBUFFER() \
+GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_READ_FRAMEBUFFER, GL_READ_FRAMEBUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindFramebuffer);\
+GFXGLPreserveInteger TORQUE_CONCAT(preserve2_, __LINE__) (GL_DRAW_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER_BINDING, (GFXGLPreserveInteger::BindFn)glBindFramebuffer)
+
+
+#if TORQUE_DEBUG
+
+    // Handy macro for checking the status of a framebuffer.  Framebuffers can fail in 
+    // all sorts of interesting ways, these are just the most common.  Further, no existing GL profiling 
+    // tool catches framebuffer errors when the framebuffer is created, so we actually need this.
+    #define CHECK_FRAMEBUFFER_STATUS()\
+    {\
+    GLenum status;\
+    status = glCheckFramebufferStatus(GL_FRAMEBUFFER);\
+    switch(status) {\
+    case GL_FRAMEBUFFER_COMPLETE:\
+    break;\
+    case GL_FRAMEBUFFER_UNSUPPORTED:\
+    AssertFatal(false, "Unsupported FBO");\
+    break;\
+    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\
+    AssertFatal(false, "Incomplete FBO Attachment");\
+    break;\
+    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\
+    AssertFatal(false, "Incomplete FBO Missing Attachment");\
+    break;\
+    case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:\
+    AssertFatal(false, "Incomplete FBO Draw buffer");\
+    break;\
+    case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:\
+    AssertFatal(false, "Incomplete FBO Read buffer");\
+    break;\
+    default:\
+    /* programming error; will fail on all hardware */\
+    AssertFatal(false, "Something really bad happened with an FBO");\
+    }\
+    }
+#else
+    #define CHECK_FRAMEBUFFER_STATUS()
+#endif //TORQUE_DEBUG
+
+#endif