//-----------------------------------------------------------------------------
// Copyright (c) 2013 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 "math/mPoint.h"
#include "graphics/TextureManager.h"
#include "graphics/dgl.h"
#include "graphics/color.h"
#include "math/mPoint.h"
#include "math/mRect.h"
#include "graphics/gFont.h"
#include "console/console.h"
#include "math/mMatrix.h"
#include "memory/frameAllocator.h"
#include "debug/profiler.h"
#include "string/unicode.h"

#include "dglMac_ScriptBinding.h"
#include "dgl_ScriptBinding.h"

namespace {

ColorI sg_bitmapModulation(255, 255, 255, 255);
ColorI sg_textAnchorColor(255, 255, 255, 255);
ColorI sg_stackColor(255, 255, 255, 255);
RectI sgCurrentClipRect;

} // namespace {}


//--------------------------------------------------------------------------
void dglSetBitmapModulation(const ColorF& in_rColor)
{
   ColorF c = in_rColor;
   c.clamp();
   sg_bitmapModulation = c;
   sg_textAnchorColor = sg_bitmapModulation;
}

void dglGetBitmapModulation(ColorF* color)
{
   *color = sg_bitmapModulation;
}

void dglGetBitmapModulation(ColorI* color)
{
   *color = sg_bitmapModulation;
}

void dglClearBitmapModulation()
{
   sg_bitmapModulation.set(255, 255, 255, 255);
}

void dglSetTextAnchorColor(const ColorF& in_rColor)
{
   ColorF c = in_rColor;
   c.clamp();
   sg_textAnchorColor = c;
}


//--------------------------------------------------------------------------
void dglDrawBitmapStretchSR(TextureObject* texture,
                       const RectI&   dstRect,
                       const RectI&   srcRect,
                       const U32   in_flip,
                       F32			 fSpin,
                       bool				bSilhouette)
{	
   AssertFatal(texture != NULL, "GSurface::drawBitmapStretchSR: NULL Handle");
   if(!dstRect.isValidRect())
      return;
   AssertFatal(srcRect.isValidRect() == true,
               "GSurface::drawBitmapStretchSR: routines assume normal rects");

   glDisable(GL_LIGHTING);

   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, texture->getGLTextureName());
   //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

   if (bSilhouette)
   {
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
   
      ColorF kModulationColor;
      dglGetBitmapModulation(&kModulationColor);
      glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, kModulationColor.address());
   }
   else
   {
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   }
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   F32 texLeft   = F32(srcRect.point.x)                    / F32(texture->getTextureWidth());
   F32 texRight  = F32(srcRect.point.x + srcRect.extent.x) / F32(texture->getTextureWidth());
   F32 texTop    = F32(srcRect.point.y)                    / F32(texture->getTextureHeight());
   F32 texBottom = F32(srcRect.point.y + srcRect.extent.y) / F32(texture->getTextureHeight());
     Point2F scrPoints[4];
   if(fSpin == 0.0f)
   {
            scrPoints[0].x = (F32)dstRect.point.x;
            scrPoints[0].y = (F32)dstRect.point.y;
            scrPoints[1].x = (F32)(dstRect.point.x + dstRect.extent.x);
            scrPoints[1].y = (F32)dstRect.point.y;
            scrPoints[2].x = (F32)dstRect.point.x;
            scrPoints[2].y = (F32)(dstRect.point.y + dstRect.extent.y);
            scrPoints[3].x = (F32)(dstRect.point.x + dstRect.extent.x);
            scrPoints[3].y = (F32)(dstRect.point.y + dstRect.extent.y);
            //screenLeft   = dstRect.point.x;
            //screenRight  = dstRect.point.x + dstRect.extent.x;
            //screenTop    = dstRect.point.y;
            //screenBottom = dstRect.point.y + dstRect.extent.y;
        }
        else
   {
     //WE NEED TO IMPLEMENT A FAST 2D ROTATION -- NOT THIS SLOWER 3D ROTATION
     MatrixF rotMatrix( EulerF( 0.0, 0.0, mDegToRad(fSpin) ) );

     Point3F offset( dstRect.point.x + dstRect.extent.x / 2.0f,
                                         dstRect.point.y + dstRect.extent.y / 2.0f, 0.0 );
         Point3F points[4];
   
     points[0] = Point3F(-dstRect.extent.x / 2.0f,  dstRect.extent.y / 2.0f, 0.0);
     points[1] = Point3F( dstRect.extent.x / 2.0f,  dstRect.extent.y / 2.0f, 0.0);
     points[2] = Point3F(-dstRect.extent.x / 2.0f, -dstRect.extent.y / 2.0f, 0.0);
     points[3] = Point3F( dstRect.extent.x / 2.0f, -dstRect.extent.y / 2.0f, 0.0);

     for( int i=0; i<4; i++ )
     {
        rotMatrix.mulP( points[i] );
        points[i] += offset;
        scrPoints[i].x = points[i].x;
        scrPoints[i].y = points[i].y;
     }
   }
  

   if(in_flip & GFlip_X)
   {
      F32 temp = texLeft;
      texLeft = texRight;
      texRight = temp;
   }
   if(in_flip & GFlip_Y)
   {
      F32 temp = texTop;
      texTop = texBottom;
      texBottom = temp;
   }

   glColor4ub(sg_bitmapModulation.red,
             sg_bitmapModulation.green,
             sg_bitmapModulation.blue,
             sg_bitmapModulation.alpha);

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)

    GLfloat verts[] = {
        (GLfloat)scrPoints[0].x, (GLfloat)scrPoints[0].y,
        (GLfloat)scrPoints[1].x, (GLfloat)scrPoints[1].y,
        (GLfloat)scrPoints[2].x, (GLfloat)scrPoints[2].y,
        (GLfloat)scrPoints[3].x, (GLfloat)scrPoints[3].y,
    };
    GLfloat texVerts[] = {
        (GLfloat)texLeft, (GLfloat)texTop,
        (GLfloat)texRight, (GLfloat)texTop,		
        (GLfloat)texLeft, (GLfloat)texBottom,
        (GLfloat)texRight, (GLfloat)texBottom,
    };
    
    
    glDisableClientState(GL_COLOR_ARRAY);
    //glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    glVertexPointer(2, GL_FLOAT, 0, verts);
    glTexCoordPointer(2, GL_FLOAT, 0, texVerts);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

#else
   glBegin(GL_TRIANGLE_FAN);
      glTexCoord2f(texLeft, texBottom);
      glVertex2f(scrPoints[2].x, scrPoints[2].y);

      glTexCoord2f(texRight, texBottom);
      glVertex2f(scrPoints[3].x, scrPoints[3].y);

      glTexCoord2f(texRight, texTop);
      glVertex2f(scrPoints[1].x, scrPoints[1].y);

      glTexCoord2f(texLeft, texTop);
      glVertex2f(scrPoints[0].x, scrPoints[0].y);
   glEnd();
#endif
   if (bSilhouette)
   {
      glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, ColorF(0.0f, 0.0f, 0.0f, 0.0f).address());
   }

   glDisable(GL_BLEND);
   glDisable(GL_TEXTURE_2D);
}

void dglDrawBitmap(TextureObject* texture, const Point2I& in_rAt, const U32 in_flip)
{
   AssertFatal(texture != NULL, "GSurface::drawBitmap: NULL Handle");

   // All non-StretchSR bitmaps are transformed into StretchSR calls...
   //
   RectI subRegion(0, 0,
                   texture->getBitmapWidth(),
                   texture->getBitmapHeight());
   RectI stretch(in_rAt.x, in_rAt.y,
                   texture->getBitmapWidth(),
                   texture->getBitmapHeight());
   dglDrawBitmapStretchSR(texture,
                          stretch,
                          subRegion,
                          in_flip);
}

void dglDrawBitmapTile(TextureObject* texture, const RectI& dstRect, const U32 in_flip, F32 fSpin, bool bSilhouette)
{
   AssertFatal(texture != NULL, "GSurface::drawBitmapTile: NULL Handle");
   // since the texture coords are calculated from the texture sub-rect we pass in,
   // but no actual indexing of that sub-rect happens, we can pass any texture coords
   // that we like to dglDrawBitmapSR(). We use this to force tiling.
   RectI subregion(0,0, dstRect.extent.x, dstRect.extent.y);
   dglDrawBitmapStretchSR(texture, dstRect, subregion, in_flip, fSpin, bSilhouette);
}

void dglDrawBitmapStretch(TextureObject* texture, const RectI& dstRect, const U32 in_flip, F32 fSpin, bool bSilhouette)
{
   AssertFatal(texture != NULL, "GSurface::drawBitmapStretch: NULL Handle");
   AssertFatal(dstRect.isValidRect() == true,
               "GSurface::drawBitmapStretch: routines assume normal rects");

   RectI subRegion(0, 0,
                   texture->getBitmapWidth(),
                   texture->getBitmapHeight());
   dglDrawBitmapStretchSR(texture,
                          dstRect,
                          subRegion,
                          in_flip,
                          fSpin,
                          bSilhouette);
}

void dglDrawBitmapSR(TextureObject *texture, const Point2I& in_rAt, const RectI& srcRect, const U32 in_flip)
{
   AssertFatal(texture != NULL, "GSurface::drawBitmapSR: NULL Handle");
   AssertFatal(srcRect.isValidRect() == true,
               "GSurface::drawBitmapSR: routines assume normal rects");

   RectI stretch(in_rAt.x, in_rAt.y,
                 srcRect.len_x(),
                 srcRect.len_y());
   dglDrawBitmapStretchSR(texture,
                          stretch,
                          srcRect,
                          in_flip);
}

U32 dglDrawText(GFont*   font,
          const Point2I& ptDraw,
          const UTF16*    in_string,
          const ColorI*  colorTable,
          const U32      maxColorIndex,
          F32            rot)
{
   return dglDrawTextN(font, ptDraw, in_string, dStrlen(in_string), colorTable, maxColorIndex, rot);
}

U32 dglDrawText(GFont*   font,
          const Point2I& ptDraw,
          const UTF8*    in_string,
          const ColorI*  colorTable,
          const U32      maxColorIndex,
          F32            rot)
{
   // Just a note - dStrlen(utf8) isn't strictly correct but it's guaranteed to be
   // as long or longer than the real length. dglDrawTextN fails gracefully
   // if you specify overlong, so this is ok.
   return dglDrawTextN(font, ptDraw, in_string, dStrlen((const UTF8 *) in_string), colorTable, maxColorIndex, rot);
}

struct TextVertex
{
   Point2F p;
   Point2F t;
   ColorI c;
    TextVertex() { set( 0.0f, 0.0f, 0.0f, 0.0f, ColorI(0, 0, 0) ); }
   void set(F32 x, F32 y, F32 tx, F32 ty, ColorI color)
   {
      p.x = x;
      p.y = y;
      t.x = tx;
      t.y = ty;
      c = color;
   }
};

//------------------------------------------------------------------------------

U32 dglDrawTextN(GFont*          font,
                 const Point2I&  ptDraw,
                 const UTF8*     in_string,
                 U32             n,
                 const ColorI*   colorTable,
                 const U32       maxColorIndex,
                 F32             rot)
{
   PROFILE_START(DrawText_UTF8);
   
   U32 len = dStrlen(in_string) + 1;
   FrameTemp<UTF16> ubuf(len);
   convertUTF8toUTF16(in_string, ubuf, len);
   U32 tmp = dglDrawTextN(font, ptDraw, ubuf, n, colorTable, maxColorIndex, rot);

   PROFILE_END();

   return tmp;
}

//-----------------------------------------------------------------------------

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
U32 dglDrawTextN(GFont*          font,
                 const Point2I&  ptDraw,
                 const UTF16*    in_string,
                 U32             n,
                 const ColorI*   colorTable,
                 const U32       maxColorIndex,
                 F32             rot)

{
   // return on zero length strings
   if( n < 1 )
      return ptDraw.x;


   MatrixF rotMatrix( EulerF( 0.0, 0.0, mDegToRad( rot ) ) );
   Point3F offset( ptDraw.x, ptDraw.y, 0.0 );
   Point3F points[4];

   U32 nCharCount = 0;

   Point2I     pt;
   UTF16       c;
   pt.x                 = 0;

   ColorI                  currentColor;
   S32                     currentPt = 0;

   TextureObject *lastTexture = NULL;

   currentColor      = sg_bitmapModulation;

   FrameTemp<TextVertex> vert(4*n);

   glDisable(GL_LIGHTING);

   glEnable(GL_TEXTURE_2D);
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glEnable(GL_BLEND);

   //Luma: Optimise by setting states once before inner loop
   glEnableClientState ( GL_VERTEX_ARRAY );
   glEnableClientState ( GL_COLOR_ARRAY );
   glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
   glVertexPointer     ( 2, GL_FLOAT, sizeof(TextVertex), &(vert[0].p) );
   glColorPointer      ( 4, GL_UNSIGNED_BYTE, sizeof(TextVertex), &(vert[0].c) );
   glTexCoordPointer   ( 2, GL_FLOAT, sizeof(TextVertex), &(vert[0].t) );

   // first build the point, color, and coord arrays
   U32 i;

   for(i = 0,c = in_string[i];in_string[i] && i < n;i++,c = in_string[i])
   {
      nCharCount++;
      if(nCharCount > n)
          break;

      // We have to do a little dance here since \t = 0x9, \n = 0xa, and \r = 0xd
      if ((c >=  1 && c <=  7) ||
         (c >= 11 && c <= 12) ||
         (c == 14))
      {
         // Color code
         if (colorTable)
         {
            static U8 remap[15] =
            {
               0x0, // 0 special null terminator
               0x0, // 1 ascii start-of-heading??
               0x1, 
               0x2, 
               0x3, 
               0x4, 
               0x5, 
               0x6, 
               0x0, // 8 special backspace
               0x0, // 9 special tab
               0x0, // a special \n
               0x7, 
               0x8,
               0x0, // a special \r
               0x9 
            };

            U8 remapped = remap[c];
            // Ignore if the color is greater than the specified max index:
            if ( remapped <= maxColorIndex )
            {
               const ColorI &clr = colorTable[remapped];
               sg_bitmapModulation = clr;
               currentColor = clr;
            }
         }
         continue;
      }

      // reset color?
      if ( c == 15 )
      {
         currentColor = sg_textAnchorColor;
         sg_bitmapModulation = sg_textAnchorColor;
         continue;
      }

      // push color:
      if ( c == 16 )
      {
         sg_stackColor = sg_bitmapModulation;
         continue;
      }

      // pop color:
      if ( c == 17 )
      {
         currentColor = sg_stackColor;
         sg_bitmapModulation = sg_stackColor;
         continue;
      }

      // Tab character
      if ( c == dT('\t') ) 
      {
          const PlatformFont::CharInfo &ci = font->getCharInfo( dT(' ') );
          pt.x += ci.xIncrement * GFont::TabWidthInSpaces;
          continue;
      }

      if( !font->isValidChar( c ) )  
         continue;

      const PlatformFont::CharInfo &ci = font->getCharInfo(c);

      if(ci.bitmapIndex == -1)
      {
         pt.x += ci.xOrigin + ci.xIncrement;
         continue;
      }

      TextureObject *newObj = font->getTextureHandle(ci.bitmapIndex);
      if(newObj != lastTexture)
      {
         if(currentPt)
         {
            glBindTexture(GL_TEXTURE_2D, lastTexture->getGLTextureName());

            //Luma:	More optimal rendering
            for (S32 i=0; i<currentPt; i+=4) 
            {
                glDrawArrays(GL_TRIANGLE_STRIP, i, 4);
            }
            currentPt = 0;
         }
         lastTexture = newObj;
      }
      if(ci.width != 0 && ci.height != 0)
      {
         pt.y = font->getBaseline() - ci.yOrigin;
         pt.x += ci.xOrigin;

         F32 texLeft   = F32(ci.xOffset)             / F32(lastTexture->getTextureWidth());
         F32 texRight  = F32(ci.xOffset + ci.width)  / F32(lastTexture->getTextureWidth());
         F32 texTop    = F32(ci.yOffset)             / F32(lastTexture->getTextureHeight());
         F32 texBottom = F32(ci.yOffset + ci.height) / F32(lastTexture->getTextureHeight());

         F32 screenLeft   = pt.x;
         F32 screenRight  = pt.x + ci.width;
         F32 screenTop    = pt.y;
         F32 screenBottom = pt.y + ci.height;

         points[0] = Point3F(screenLeft, screenTop, 0.0);
         points[1] = Point3F(screenRight,  screenTop, 0.0);
         points[2] = Point3F( screenLeft,  screenBottom, 0.0);
         points[3] = Point3F( screenRight, screenBottom, 0.0);

         for( int i=0; i<4; i++ )
         {
            rotMatrix.mulP( points[i] );
            points[i] += offset;
         }
         vert[currentPt++].set(points[0].x, points[0].y, texLeft, texTop, currentColor);
         vert[currentPt++].set(points[1].x, points[1].y, texRight, texTop, currentColor);
         vert[currentPt++].set(points[2].x, points[2].y, texLeft, texBottom, currentColor);
         vert[currentPt++].set(points[3].x, points[3].y, texRight, texBottom, currentColor);
         pt.x += ci.xIncrement - ci.xOrigin;
      }
      else
         pt.x += ci.xIncrement;
   }
   if(currentPt)
   {
       //Luma:	More optimal rendering
       glBindTexture(GL_TEXTURE_2D, lastTexture->getGLTextureName());
       for (S32 i=0; i<currentPt; i+=4) 
       {
            glDrawArrays(GL_TRIANGLE_STRIP, i, 4);
       }
   }

   glDisableClientState ( GL_VERTEX_ARRAY );
   glDisableClientState ( GL_COLOR_ARRAY );
   glDisableClientState ( GL_TEXTURE_COORD_ARRAY );

   glDisable(GL_BLEND);
   glDisable(GL_TEXTURE_2D);

   pt.x += ptDraw.x; // DAW: Account for the fact that we removed the drawing point from the text start at the beginning.

   AssertFatal(pt.x >= ptDraw.x, "How did this happen?");
   PROFILE_END();

   return pt.x - ptDraw.x;
}

#else

U32 dglDrawTextN(GFont*          font,
                 const Point2I&  ptDraw,
                 const UTF16*    in_string,
                 U32             n,
                 const ColorI*   colorTable,
                 const U32       maxColorIndex,
                 F32             rot)
{
   // return on zero length strings
   if( n < 1 )
      return ptDraw.x;
   PROFILE_START(DrawText);

   MatrixF rotMatrix( EulerF( 0.0, 0.0, mDegToRad( rot ) ) );
   Point3F offset( (F32)ptDraw.x, (F32)ptDraw.y, 0.0f );
   Point3F points[4];

   U32 nCharCount = 0;

   Point2I     pt;
   UTF16       c;
   pt.x                 = 0;

   ColorI                  currentColor;
   S32                     currentPt = 0;

   TextureObject *lastTexture = NULL;

   currentColor      = sg_bitmapModulation;

   FrameTemp<TextVertex> vert(4*n);

   glDisable(GL_LIGHTING);

   glEnable(GL_TEXTURE_2D);
   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glEnable(GL_BLEND);

   glEnableClientState ( GL_VERTEX_ARRAY );
   glVertexPointer     ( 2, GL_FLOAT, sizeof(TextVertex), &(vert[0].p) );

   glEnableClientState ( GL_COLOR_ARRAY );
   glColorPointer      ( 4, GL_UNSIGNED_BYTE, sizeof(TextVertex), &(vert[0].c) );

   glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
   glTexCoordPointer   ( 2, GL_FLOAT, sizeof(TextVertex), &(vert[0].t) );

   // first build the point, color, and coord arrays
   U32 i;

   for(i = 0,c = in_string[i];in_string[i] && i < n;i++,c = in_string[i])
   {
      nCharCount++;
      if(nCharCount > n)
          break;

      // We have to do a little dance here since \t = 0x9, \n = 0xa, and \r = 0xd
      if ((c >=  1 && c <=  7) ||
         (c >= 11 && c <= 12) ||
         (c == 14))
      {
         // Color code
         if (colorTable)
         {
            static U8 remap[15] =
            {
               0x0, // 0 special null terminator
               0x0, // 1 ascii start-of-heading??
               0x1, 
               0x2, 
               0x3, 
               0x4, 
               0x5, 
               0x6, 
               0x0, // 8 special backspace
               0x0, // 9 special tab
               0x0, // a special \n
               0x7, 
               0x8,
               0x0, // a special \r
               0x9 
            };

            U8 remapped = remap[c];
            // Ignore if the color is greater than the specified max index:
            if ( remapped <= maxColorIndex )
            {
               const ColorI &clr = colorTable[remapped];
               sg_bitmapModulation = clr;
               currentColor = clr;
            }
         }
         continue;
      }

      // reset color?
      if ( c == 15 )
      {
         currentColor = sg_textAnchorColor;
         sg_bitmapModulation = sg_textAnchorColor;
         continue;
      }

      // push color:
      if ( c == 16 )
      {
         sg_stackColor = sg_bitmapModulation;
         continue;
      }

      // pop color:
      if ( c == 17 )
      {
         currentColor = sg_stackColor;
         sg_bitmapModulation = sg_stackColor;
         continue;
      }

      // Tab character
      if ( c == dT('\t') ) 
      {
          const PlatformFont::CharInfo &ci = font->getCharInfo( dT(' ') );
          pt.x += ci.xIncrement * GFont::TabWidthInSpaces;
          continue;
      }

      if( !font->isValidChar( c ) )  
         continue;

      const PlatformFont::CharInfo &ci = font->getCharInfo(c);

      if(ci.bitmapIndex == -1)
      {
         pt.x += ci.xOrigin + ci.xIncrement;
         continue;
      }

      TextureObject *newObj = font->getTextureHandle(ci.bitmapIndex);
      if(newObj != lastTexture)
      {
         if(currentPt)
         {
            glBindTexture(GL_TEXTURE_2D, lastTexture->getGLTextureName());
            glDrawArrays( GL_QUADS, 0, currentPt );
            currentPt = 0;
         }
         lastTexture = newObj;
      }
      if(ci.width != 0 && ci.height != 0)
      {
         pt.y = font->getBaseline() - ci.yOrigin;
         pt.x += ci.xOrigin;

         F32 texLeft   = F32(ci.xOffset)             / F32(lastTexture->getTextureWidth());
         F32 texRight  = F32(ci.xOffset + ci.width)  / F32(lastTexture->getTextureWidth());
         F32 texTop    = F32(ci.yOffset)             / F32(lastTexture->getTextureHeight());
         F32 texBottom = F32(ci.yOffset + ci.height) / F32(lastTexture->getTextureHeight());

         F32 screenLeft   = (F32)pt.x;
         F32 screenRight  = (F32)(pt.x + ci.width);
         F32 screenTop    = (F32)pt.y;
         F32 screenBottom = (F32)(pt.y + ci.height);

         points[0] = Point3F(screenLeft, screenBottom, 0.0);
         points[1] = Point3F(screenRight,  screenBottom, 0.0);
         points[2] = Point3F( screenRight,  screenTop, 0.0);
         points[3] = Point3F( screenLeft, screenTop, 0.0);

         for( int i=0; i<4; i++ )
         {
            rotMatrix.mulP( points[i] );
            points[i] += offset;
         }

         vert[currentPt++].set(points[0].x, points[0].y, texLeft, texBottom, currentColor);
         vert[currentPt++].set(points[1].x, points[1].y, texRight, texBottom, currentColor);
         vert[currentPt++].set(points[2].x, points[2].y, texRight, texTop, currentColor);
         vert[currentPt++].set(points[3].x, points[3].y, texLeft, texTop, currentColor);
         pt.x += ci.xIncrement - ci.xOrigin;
      }
      else
         pt.x += ci.xIncrement;
   }
   if(currentPt)
   {
      glBindTexture(GL_TEXTURE_2D, lastTexture->getGLTextureName());
      glDrawArrays( GL_QUADS, 0, currentPt );
   }

   glDisableClientState ( GL_VERTEX_ARRAY );
   glDisableClientState ( GL_COLOR_ARRAY );
   glDisableClientState ( GL_TEXTURE_COORD_ARRAY );

   glDisable(GL_BLEND);
   glDisable(GL_TEXTURE_2D);

   pt.x += ptDraw.x; // DAW: Account for the fact that we removed the drawing point from the text start at the beginning.

   AssertFatal(pt.x >= ptDraw.x, "How did this happen?");
   PROFILE_END();

   return pt.x - ptDraw.x;
}
#endif

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //
// Drawing primitives

void dglDrawLine(S32 x1, S32 y1, S32 x2, S32 y2, const ColorI &color)
{
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glDisable(GL_TEXTURE_2D);

   glColor4ub(color.red, color.green, color.blue, color.alpha);
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
    GLfloat verts[] = {
        (GLfloat)(x1 + 0.5f), (GLfloat)(y1 + 0.5f),
        (GLfloat)(x2 + 0.5f), (GLfloat)(y2 + 0.5f),
    };
    
    glVertexPointer(2, GL_FLOAT, 0, verts );
    
    glDrawArrays(GL_LINES, 0, 2);//draw last two
#else
   glBegin(GL_LINES);
   glVertex2f((F32)x1 + 0.5f,  (F32)y1 + 0.5f);
   glVertex2f((F32)x2 + 0.5f,    (F32)y2 + 0.5f);
   glEnd();
    //glBegin(GL_POINTS);
    //glVertex2f((F32)x2 + 0.5, (F32)y2 + 0.5);
    //glEnd();
#endif
}

void dglDrawLine(const Point2I &startPt, const Point2I &endPt, const ColorI &color)
{
    dglDrawLine(startPt.x, startPt.y, endPt.x, endPt.y, color);
}

void dglDrawRect(const Point2I &upperL, const Point2I &lowerR, const ColorI &color, const float &lineWidth)
{
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glDisable(GL_TEXTURE_2D);

   glLineWidth(lineWidth);

   glColor4ub(color.red, color.green, color.blue, color.alpha);
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
    GLfloat verts[] = {
        (GLfloat)(upperL.x), (GLfloat)(upperL.y),
        (GLfloat)(lowerR.x), (GLfloat)(upperL.y),
        (GLfloat)(lowerR.x), (GLfloat)(lowerR.y),
        (GLfloat)(upperL.x), (GLfloat)(lowerR.y),
    };
    
    glVertexPointer(2, GL_FLOAT, 0, verts );
    glDrawArrays(GL_LINE_LOOP, 0, 4 );//draw last two
#else
   glBegin(GL_LINE_LOOP);
      glVertex2f((F32)upperL.x + 0.5f, (F32)upperL.y + 0.5f);
      glVertex2f((F32)lowerR.x + 0.5f, (F32)upperL.y + 0.5f);
      glVertex2f((F32)lowerR.x + 0.5f, (F32)lowerR.y + 0.5f);
      glVertex2f((F32)upperL.x + 0.5f, (F32)lowerR.y + 0.5f);
   glEnd();
#endif
}

// the fill convention for lined rects is that they outline the rectangle border of the
// filled region specified.

void dglDrawRect(const RectI &rect, const ColorI &color, const float &lineWidth)
{
   Point2I lowerR(rect.point.x + rect.extent.x - 1, rect.point.y + rect.extent.y - 1);
   dglDrawRect(rect.point, lowerR, color, lineWidth);
}

// the fill convention says that pixel at upperL will be filled and
// that pixel at lowerR will NOT be filled.

void dglDrawRectFill(const Point2I &upperL, const Point2I &lowerR, const ColorI &color)
{
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glDisable(GL_TEXTURE_2D);

   glColor4ub(color.red, color.green, color.blue, color.alpha);
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
    GLfloat vertices[] = {
        (GLfloat)upperL.x, (GLfloat)upperL.y,
        (GLfloat)upperL.x, (GLfloat)lowerR.y,
        (GLfloat)lowerR.x, (GLfloat)upperL.y,
        (GLfloat)lowerR.x, (GLfloat)lowerR.y,
    };
    
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#else
   glRecti((S32)upperL.x, (S32)upperL.y, (S32)lowerR.x, (S32)lowerR.y);
#endif
}
void dglDrawRectFill(const RectI &rect, const ColorI &color)
{
   Point2I lowerR(rect.point.x + rect.extent.x, rect.point.y + rect.extent.y);
   dglDrawRectFill(rect.point, lowerR, color);
}

void dglDraw2DSquare( const Point2F &screenPoint, F32 width, F32 spinAngle )
{
   width *= 0.5;

   MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) );

   Point3F offset( screenPoint.x, screenPoint.y, 0.0 );
   Point3F points[4];

   points[0] = Point3F(-width, -width, 0.0);
   points[1] = Point3F(-width,  width, 0.0);
   points[2] = Point3F( width,  width, 0.0);
   points[3] = Point3F( width, -width, 0.0);

   for( int i=0; i<4; i++ )
   {
      rotMatrix.mulP( points[i] );
      points[i] += offset;
   }

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
    GLfloat verts[] = {
        0.0, 0.0,
        1.0, 0.0,
        0.0, 1.0,//may need to switch last two
        1.0, 1.0,
    };

    GLfloat texVerts[] = {
        (GLfloat)points[0].x, (GLfloat)points[0].y,
        (GLfloat)points[1].x, (GLfloat)points[1].y,
        (GLfloat)points[3].x, (GLfloat)points[3].y,
        (GLfloat)points[2].x, (GLfloat)points[2].y,
    };
    
    glDisableClientState(GL_COLOR_ARRAY);
    //glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    glVertexPointer(2, GL_FLOAT, 0, verts);
    glTexCoordPointer(2, GL_FLOAT, 0, texVerts);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
#else
   glBegin(GL_TRIANGLE_FAN);
      glTexCoord2f(0.0, 0.0);
      glVertex2fv(points[0]);

      glTexCoord2f(0.0, 1.0);
      glVertex2fv(points[1]);

      glTexCoord2f(1.0, 1.0);
      glVertex2fv(points[2]);

      glTexCoord2f(1.0, 0.0);
      glVertex2fv(points[3]);
   glEnd();
#endif
}

void dglDrawBillboard( const Point3F &position, F32 width, F32 spinAngle )
{
   MatrixF modelview;
   dglGetModelview( &modelview );
   modelview.transpose();


   width *= 0.5;
   Point3F points[4];
   points[0] = Point3F(-width, 0.0, -width);
   points[1] = Point3F(-width, 0.0,  width);
   points[2] = Point3F( width, 0.0,  width);
   points[3] = Point3F( width, 0.0, -width);


   MatrixF rotMatrix( EulerF( 0.0, spinAngle, 0.0 ) );

   for( int i=0; i<4; i++ )
   {
      rotMatrix.mulP( points[i] );
      modelview.mulP( points[i] );
      points[i] += position;
   }


#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
    GLfloat verts[] = {
        0.0, 1.0,
        0.0, 0.0,
        1.0, 1.0,//may need to switch last two
        1.0, 0.0,
    };
    
    GLfloat texVerts[] = {
        (GLfloat)points[0].x, (GLfloat)points[0].y,
        (GLfloat)points[1].x, (GLfloat)points[1].y,
        (GLfloat)points[3].x, (GLfloat)points[3].y,
        (GLfloat)points[2].x, (GLfloat)points[2].y,
    };
    
    glDisableClientState(GL_COLOR_ARRAY);
    //glDisableClientState(GL_POINT_SIZE_ARRAY_OES);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    glVertexPointer(2, GL_FLOAT, 0, verts);
    glTexCoordPointer(2, GL_FLOAT, 0, texVerts);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
#else
   glBegin(GL_TRIANGLE_FAN);
      glTexCoord2f(0.0, 1.0);
      glVertex3fv(points[0]);

      glTexCoord2f(0.0, 0.0);
      glVertex3fv(points[1]);

      glTexCoord2f(1.0, 0.0);
      glVertex3fv(points[2]);

      glTexCoord2f(1.0, 1.0);
      glVertex3fv(points[3]);
   glEnd();
#endif
}

void dglWireCube(const Point3F & extent, const Point3F & center)
{
   static Point3F cubePoints[8] =
   {
      Point3F(-1, -1, -1), Point3F(-1, -1,  1), Point3F(-1,  1, -1), Point3F(-1,  1,  1),
         Point3F( 1, -1, -1), Point3F( 1, -1,  1), Point3F( 1,  1, -1), Point3F( 1,  1,  1)
   };

   static U32 cubeFaces[6][4] =
   {
      { 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
      { 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
   };

   glDisable(GL_CULL_FACE);

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
//PUAP -Mat untested
   for (S32 i = 0; i < 6; i++)
   {
       GLfloat verts[] = {
           0, 0, 0,
           0, 0, 0,
           0, 0, 0,
           0, 0, 0,
       };
       int j = 0;
      for(int vert = 0; vert < 4; vert++)
      {
         int idx = cubeFaces[i][vert];
         //glVertex3f(cubePoints[idx].x * extent.x + center.x,
            //cubePoints[idx].y * extent.y + center.y,
            //cubePoints[idx].z * extent.z + center.z);
          verts[j] =	 cubePoints[idx].x * extent.x + center.x;
          verts[++j] = cubePoints[idx].y * extent.y + center.y;
          verts[++j] = cubePoints[idx].z * extent.z + center.z;
      }
       glVertexPointer(3, GL_FLOAT, 0, verts);
       glDrawArrays(GL_LINE_LOOP, 0, 4);
   }
#else
   for (S32 i = 0; i < 6; i++)
   {
      glBegin(GL_LINE_LOOP);
      for(int vert = 0; vert < 4; vert++)
      {
         int idx = cubeFaces[i][vert];
         glVertex3f(cubePoints[idx].x * extent.x + center.x,
            cubePoints[idx].y * extent.y + center.y,
            cubePoints[idx].z * extent.z + center.z);
      }
      glEnd();
   }
#endif
}


void dglSolidCube(const Point3F & extent, const Point3F & center)
{
   static Point3F cubePoints[8] =
   {
      Point3F(-1, -1, -1), Point3F(-1, -1,  1), Point3F(-1,  1, -1), Point3F(-1,  1,  1),
         Point3F( 1, -1, -1), Point3F( 1, -1,  1), Point3F( 1,  1, -1), Point3F( 1,  1,  1)
   };

   static U32 cubeFaces[6][4] =
   {
      { 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
      { 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
   };

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
//PUAP -Mat untested
   for (S32 i = 0; i < 6; i++)
   {
       GLfloat verts[] = {
           0, 0, 0,
           0, 0, 0,
           0, 0, 0,
           0, 0, 0,
       };
       int j = 0;
      for(int vert = 0; vert < 4; vert++)
      {
         int idx = cubeFaces[i][vert];
         //glVertex3f(cubePoints[idx].x * extent.x + center.x,
            //cubePoints[idx].y * extent.y + center.y,
            //cubePoints[idx].z * extent.z + center.z);
          verts[j] = cubePoints[idx].x * extent.x + center.x;
          verts[++j] = cubePoints[idx].y * extent.y + center.y;
          verts[++j] = cubePoints[idx].z * extent.z + center.z;
      }
       glVertexPointer(3, GL_FLOAT, 0, verts);
       glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
   }
#else
   for (S32 i = 0; i < 6; i++)
   {
      glBegin(GL_TRIANGLE_FAN);
      for(int vert = 0; vert < 4; vert++)
      {
         int idx = cubeFaces[i][vert];
         glVertex3f(cubePoints[idx].x * extent.x + center.x,
            cubePoints[idx].y * extent.y + center.y,
            cubePoints[idx].z * extent.z + center.z);
      }
      glEnd();
   }
#endif
}

void dglSetClipRect(const RectI &clipRect)
{
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();

   U32 screenHeight = Platform::getWindowSize().y;

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID)
   glOrthof(clipRect.point.x, clipRect.point.x + clipRect.extent.x,
           clipRect.extent.y, 0,
           0, 1);
#else
   glOrtho(clipRect.point.x, clipRect.point.x + clipRect.extent.x,
           clipRect.extent.y, 0,
           0, 1);
#endif

   glTranslatef(0.0f, (F32)-clipRect.point.y, 0.0f);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   glViewport(clipRect.point.x, screenHeight - (clipRect.point.y + clipRect.extent.y),
              clipRect.extent.x, clipRect.extent.y);

   sgCurrentClipRect = clipRect;
}

const RectI& dglGetClipRect()
{
   return sgCurrentClipRect;
}

bool dglPointToScreen( Point3F &point3D, Point3F &screenPoint )
{
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
   GLfloat       glMV[16];
   GLfloat       glPR[16];
   GLint          glVP[4];

   glGetFloatv(GL_PROJECTION_MATRIX, glPR);
   glGetFloatv(GL_MODELVIEW_MATRIX, glMV);

   RectI viewport;
   dglGetViewport(&viewport);

   glVP[0] = viewport.point.x;
   glVP[1] = viewport.point.y + viewport.extent.y;
   glVP[2] = viewport.extent.x;
   glVP[3] = -viewport.extent.y;

   MatrixF mv;
   dglGetModelview(&mv);
   MatrixF pr;
   dglGetProjection(&pr);

   F64 x, y, z;
// PUAP -Mat untested
   int result = gluProject( point3D.x, point3D.y, point3D.z, (const F64 *)&glMV, (const F64 *)&glPR, (const GLint *)&glVP, &x, &y, &z );

   screenPoint.x = x;
   screenPoint.y = y;
   screenPoint.z = z;


   return (result == GL_TRUE);
#else
   GLdouble       glMV[16];
   GLdouble       glPR[16];
   GLint          glVP[4];


   glGetDoublev(GL_PROJECTION_MATRIX, glPR);
   glGetDoublev(GL_MODELVIEW_MATRIX, glMV);

   RectI viewport;
   dglGetViewport(&viewport);

   glVP[0] = viewport.point.x;
   glVP[1] = viewport.point.y + viewport.extent.y;
   glVP[2] = viewport.extent.x;
   glVP[3] = -viewport.extent.y;

   MatrixF mv;
   dglGetModelview(&mv);
   MatrixF pr;
   dglGetProjection(&pr);

   F64 x, y, z;
   int result = gluProject( (GLdouble)point3D.x, (GLdouble)point3D.y, (GLdouble)point3D.z, (const F64 *)&glMV, (const F64 *)&glPR, (const GLint *)&glVP, &x, &y, &z );
   screenPoint.x = (F32)x;
   screenPoint.y = (F32)y;
   screenPoint.z = (F32)z;


   return (result == GL_TRUE);
#endif
    
}



bool dglIsInCanonicalState()
{
   bool ret = true;

   // Canonical state:
   //  BLEND disabled
   //  TEXTURE_2D disabled on both texture units.
   //  ActiveTexture set to 0
   //  LIGHTING off
   //  winding : clockwise ?
   //  cullface : disabled

   ret &= glIsEnabled(GL_BLEND) == GL_FALSE;
   ret &= glIsEnabled(GL_CULL_FACE) == GL_FALSE;
   GLint temp;
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
// PUAP -Mat removed unsupported textureARB and Fog stuff
   if (dglDoesSupportARBMultitexture() == true) {
      //glActiveTextureARB(GL_TEXTURE1_ARB);
      ret &= glIsEnabled(GL_TEXTURE_2D) == GL_FALSE;
      glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &temp);
      ret &= temp == GL_REPLACE;

      //glActiveTextureARB(GL_TEXTURE0_ARB);
      ret &= glIsEnabled(GL_TEXTURE_2D) == GL_FALSE;
      glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &temp);
      ret &= temp == GL_REPLACE;

      //glClientActiveTextureARB(GL_TEXTURE1_ARB);
      ret &= glIsEnabled(GL_TEXTURE_COORD_ARRAY) == GL_FALSE;
      //glClientActiveTextureARB(GL_TEXTURE0_ARB);
      ret &= glIsEnabled(GL_TEXTURE_COORD_ARRAY) == GL_FALSE;
   } else {
      ret &= glIsEnabled(GL_TEXTURE_2D) == GL_FALSE;
      glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &temp);
      ret &= temp == GL_REPLACE;

      ret &= glIsEnabled(GL_TEXTURE_COORD_ARRAY) == GL_FALSE;
   }

   ret &= glIsEnabled(GL_LIGHTING) == GL_FALSE;

   ret &= glIsEnabled(GL_COLOR_ARRAY)         == GL_FALSE;
   ret &= glIsEnabled(GL_VERTEX_ARRAY)        == GL_FALSE;
   ret &= glIsEnabled(GL_NORMAL_ARRAY)        == GL_FALSE;
   //if (dglDoesSupportFogCoord())
      //ret &= glIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT) == GL_FALSE;

#else

   if (dglDoesSupportARBMultitexture() == true) {
      glActiveTextureARB(GL_TEXTURE1_ARB);
      ret &= glIsEnabled(GL_TEXTURE_2D) == GL_FALSE;
      glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &temp);
      ret &= temp == GL_REPLACE;

      glActiveTextureARB(GL_TEXTURE0_ARB);
      ret &= glIsEnabled(GL_TEXTURE_2D) == GL_FALSE;
      glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &temp);
      ret &= temp == GL_REPLACE;

      glClientActiveTextureARB(GL_TEXTURE1_ARB);
      ret &= glIsEnabled(GL_TEXTURE_COORD_ARRAY) == GL_FALSE;
      glClientActiveTextureARB(GL_TEXTURE0_ARB);
      ret &= glIsEnabled(GL_TEXTURE_COORD_ARRAY) == GL_FALSE;
   } else {
      ret &= glIsEnabled(GL_TEXTURE_2D) == GL_FALSE;
      glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &temp);
      ret &= temp == GL_REPLACE;

      ret &= glIsEnabled(GL_TEXTURE_COORD_ARRAY) == GL_FALSE;
   }

   ret &= glIsEnabled(GL_LIGHTING) == GL_FALSE;

   ret &= glIsEnabled(GL_COLOR_ARRAY)         == GL_FALSE;
   ret &= glIsEnabled(GL_VERTEX_ARRAY)        == GL_FALSE;
   ret &= glIsEnabled(GL_NORMAL_ARRAY)        == GL_FALSE;
   if (dglDoesSupportFogCoord())
      ret &= glIsEnabled(GL_FOG_COORDINATE_ARRAY_EXT) == GL_FALSE;
#endif
   return ret;
}


void dglSetCanonicalState()
{
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
// PUAP -Mat removed unsupported textureARB and Fog stuff
   glDisable(GL_BLEND);
   glDisable(GL_CULL_FACE);
   glBlendFunc(GL_ONE, GL_ZERO);
   glDisable(GL_LIGHTING);
   if (dglDoesSupportARBMultitexture() == true) {
      //glActiveTextureARB(GL_TEXTURE1_ARB);
      glDisable(GL_TEXTURE_2D);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
      //glActiveTextureARB(GL_TEXTURE0_ARB);
      glDisable(GL_TEXTURE_2D);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   } else {
      glDisable(GL_TEXTURE_2D);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   }

   glDisableClientState(GL_COLOR_ARRAY);
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_NORMAL_ARRAY);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   //if (dglDoesSupportFogCoord())
      //glDisableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
#else
   glDisable(GL_BLEND);
   glDisable(GL_CULL_FACE);
   glBlendFunc(GL_ONE, GL_ZERO);
   glDisable(GL_LIGHTING);
   if (dglDoesSupportARBMultitexture() == true) {
      glActiveTextureARB(GL_TEXTURE1_ARB);
      glDisable(GL_TEXTURE_2D);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
      glActiveTextureARB(GL_TEXTURE0_ARB);
      glDisable(GL_TEXTURE_2D);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   } else {
      glDisable(GL_TEXTURE_2D);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   }

   glDisableClientState(GL_COLOR_ARRAY);
   glDisableClientState(GL_VERTEX_ARRAY);
   glDisableClientState(GL_NORMAL_ARRAY);
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   if (dglDoesSupportFogCoord())
      glDisableClientState(GL_FOG_COORDINATE_ARRAY_EXT);
#endif
}

void dglGetTransformState(S32* mvDepth,
                          S32* pDepth,
                          S32* t0Depth,
                          F32* t0Matrix,
                          S32* t1Depth,
                          F32* t1Matrix,
                          S32* vp)
{
   glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, (GLint*)mvDepth);
   glGetIntegerv(GL_PROJECTION_STACK_DEPTH, (GLint*)pDepth);

   glGetIntegerv(GL_TEXTURE_STACK_DEPTH, (GLint*)t0Depth);
   glGetFloatv(GL_TEXTURE_MATRIX, t0Matrix);
   if (dglDoesSupportARBMultitexture())
   {
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
// PUAP -Mat removed unsupported textureARB stuff
      //glActiveTextureARB(GL_TEXTURE1_ARB);
      glGetIntegerv(GL_TEXTURE_STACK_DEPTH, (GLint*)t1Depth);
      glGetFloatv(GL_TEXTURE_MATRIX, t1Matrix);
      //glActiveTextureARB(GL_TEXTURE0_ARB);
#else
      glActiveTextureARB(GL_TEXTURE1_ARB);
      glGetIntegerv(GL_TEXTURE_STACK_DEPTH, (GLint*)t1Depth);
      glGetFloatv(GL_TEXTURE_MATRIX, t1Matrix);
      glActiveTextureARB(GL_TEXTURE0_ARB);
#endif
   }
   else
   {
      *t1Depth = 0;
      for (U32 i = 0; i < 16; i++)
         t1Matrix[i] = 0;
   }

   RectI v;
   dglGetViewport(&v);
   vp[0] = v.point.x;
   vp[1] = v.point.y;
   vp[2] = v.extent.x;
   vp[3] = v.extent.y;
}


bool dglCheckState(const S32 mvDepth, const S32 pDepth,
                   const S32 t0Depth, const F32* t0Matrix,
                   const S32 t1Depth, const F32* t1Matrix,
                   const S32* vp)
{
   GLint md, pd;
   RectI v;

   glGetIntegerv(GL_MODELVIEW_STACK_DEPTH,  &md);
   glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &pd);

   GLint t0d, t1d;
   GLfloat t0m[16], t1m[16];
   glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &t0d);
   glGetFloatv(GL_TEXTURE_MATRIX, t0m);
   if (dglDoesSupportARBMultitexture())
   {
#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
// PUAP -Mat removed unsupported textureARB and Fog stuff
      //glActiveTextureARB(GL_TEXTURE1_ARB);
      glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &t1d);
      glGetFloatv(GL_TEXTURE_MATRIX, t1m);
      //glActiveTextureARB(GL_TEXTURE0_ARB);
#else
      glActiveTextureARB(GL_TEXTURE1_ARB);
      glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &t1d);
      glGetFloatv(GL_TEXTURE_MATRIX, t1m);
      glActiveTextureARB(GL_TEXTURE0_ARB);
#endif
   }
   else
   {
      t1d = 0;
      for (U32 i = 0; i < 16; i++)
         t1m[i] = 0;
   }

   dglGetViewport(&v);

   return ((md == mvDepth) &&
           (pd == pDepth) &&
           (t0d == t0Depth) &&
           (dMemcmp(t0m, t0Matrix, sizeof(F32) * 16) == 0) &&
           (t1d == t1Depth) &&
           (dMemcmp(t1m, t1Matrix, sizeof(F32) * 16) == 0) &&
           ((v.point.x  == vp[0]) &&
            (v.point.y  == vp[1]) &&
            (v.extent.x == vp[2]) &&
            (v.extent.y == vp[3])));
}

#if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_ANDROID) || defined(TORQUE_OS_EMSCRIPTEN)
GLfloat gVertexFloats[8];
GLfloat gTextureVerts[8];
#endif
