ecere/gfx/fontRendering: Improved rendering of some fonts e.g. MS Sans Serif having...
[sdk] / ecere / src / gfx / drivers / OpenGLDisplayDriver.ec
index 5734a38..289d981 100644 (file)
@@ -1,56 +1,50 @@
-namespace gfx::drivers;
+#if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
 
-#if defined(_GLES)
-#define ES1_1
-#else
-// #define SHADERS
+// #define DIAGNOSTICS
+#if defined(_DEBUG) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
+ #define GL_DEBUGGING
 #endif
 
-#define GL_BGRA_EXT  0x80E1
-
-#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
-#  if defined(SHADERS)
-#     include "gl_core_3_3.h"
-#  else
-#     include "gl_compat_4_4.h"
-#  endif
-#endif
+import "Display"
 
 import "glab"
 import "immediate"
 import "matrixStack"
-import "shading"
+import "defaultShader"
 
-#ifdef SHADERS
+namespace gfx::drivers;
 
-#undef glEnableClientState
-#undef glDisableClientState
-#undef GL_VERTEX_ARRAY
-#undef GL_NORMAL_ARRAY
-#undef GL_TEXTURE_COORD_ARRAY
-#undef GL_COLOR_ARRAY
+#include "gl123es.h"
 
-#define glEnableClientState      glEnableVertexAttribArray
-#define glDisableClientState     glDisableVertexAttribArray
-#define GL_VERTEX_ARRAY          GLBufferContents::vertex
-#define GL_NORMAL_ARRAY          GLBufferContents::normal
-#define GL_TEXTURE_COORD_ARRAY   GLBufferContents::texCoord
-#define GL_COLOR_ARRAY           GLBufferContents::color
+// **********   GL PLATFORMS INCLUDES   **********
+// UNIX
+#if defined(__unix__) || defined(__APPLE__)
 
-#endif
+   // EGL
+   #if defined(__ANDROID__) || defined(__ODROID__)
+      import "egl"
 
-// We were using PBUFFER for alpha compositing on Linux before, but it does not seem to work, nor be required anymore.
-// #define USEPBUFFER
-#if defined(__unix__) || defined(__APPLE__)
+      #if defined(__ANDROID__)
+         #include <android/native_activity.h>
+         #include <android/log.h>
+         #define printf(...)  ((void)__android_log_print(ANDROID_LOG_VERBOSE, "ecere-app", __VA_ARGS__))
+      #endif
 
-#if !defined(__MINGW32__)
-   #define GL_GLEXT_PROTOTYPES
-#endif
+   // Emscripten
+   #elif defined(__EMSCRIPTEN__)
+      #define property _property
+      #define uint _uint
 
-#define pointer _pointer
+      #include <emscripten/emscripten.h>
+      #include <emscripten/html5.h>
 
+      #undef property
+      #undef uint
 
-   #if !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+   // GLX
+   #elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+      #define pointer _pointer
+      #define GL_GLEXT_PROTOTYPES
 
       #define property _property
       #define new _new
@@ -87,306 +81,168 @@ import "shading"
       #undef new
       #undef property
       #undef class
+      #undef pointer
 
-   #endif
+      #if !defined(__APPLE__)
+      default GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count);
+      default GLAPI void APIENTRY glUnlockArraysEXT (void);
+      #endif
 
-#endif
+      import "XInterface"
 
-#if defined(__APPLE__)
-#include <OpenGl/gl.h>
-#endif
+      // We were using PBUFFER for alpha compositing on Linux before, but it does not seem to work, nor be required anymore.
+      // #define USEPBUFFER
 
-#if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
+   #endif
 
-#if defined(__WIN32__)
+// Apple
+#elif defined(__APPLE__)
+   #include <OpenGl/gl.h>
+
+// WGL
+#elif defined(__WIN32__)
    //#define WIN32_LEAN_AND_MEAN
    #undef _WIN32_WINNT
    #define _WIN32_WINNT 0x0502
    #define String Sting_
    #include <windows.h>
    #undef String
-#endif
-
-#if defined(__ANDROID__) || defined(__ODROID__)
-
-#define uint _uint
-#define property _property
-#define new _new
-#define class _class
-#define Window    X11Window
-#define Cursor    X11Cursor
-#define Font      X11Font
-#define Display   X11Display
-#define Time      X11Time
-#define KeyCode   X11KeyCode
-#define Picture   X11Picture
-#define Bool      X11Bool
-
-   #include <GLES/gl.h>
-
-#undef Bool
-#undef Picture
-#undef Window
-#undef Cursor
-#undef Font
-#undef Display
-#undef Time
-#undef KeyCode
-#undef uint
-#undef new
-#undef property
-#undef class
 
-#elif defined(__EMSCRIPTEN__)
-
-   #define property _property
-   #define uint _uint
-
-   #include <GL/gl.h>
-
-   //#include <GLES/gl.h>
-   //#include <EGL/egl.h>
-
-   //#include <GLES2/gl.h>
-   //#include <EGL/egl.h>
-
-   //#include <GLES2/gl2.h>
-   #include <GL/glfw.h>
-   #include <emscripten/emscripten.h>
-
-   #undef property
-   #undef uint
-
-#else
-   #include <GL/gl.h>
-#endif
-
-#if defined(__ODROID__) && !defined(ES1_1)
-#define ES1_1
+   #include "wglDefs.h"
 #endif
 
-#if defined(__EMSCRIPTEN__)
-#define EM_MODE
-// #define ES1_1
+#if defined(__WIN32__)
+#elif !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+default:
+private:
 #endif
 
-#undef pointer
-
-import "Display"
-
-#if defined(__unix__) || defined(__APPLE__)
-
-   #if !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
-   import "XInterface"
-   #endif
+/*                                                            OpenGL Versions Features Quick Reference
+
+                                 | OpenGL 1.1  | OpenGL 1.5  | GL ES 1.1  |  OpenGL 2  | GL 3 (Compat) | GL 3 (Core) |  GL ES 2    |  WebGL 1   |  GL ES 3   |   WebGL 2
+   =======================================================================================================================================================================
+   glBegin()                     |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   glLoadMatrix()                |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   glLineWidth()                 |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   glPointSize()                 |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   glLineStipple()               |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   glPolygonStipple()            |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   glColorMaterial()             |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   GL_QUADS                      |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   GL_INT / GL_DOUBLE            |      X      |      X      |     -      |     X      |       X       |      X      |      -      |     -      |     -      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   GL_SELECT                     |      X      |      X      |     -      |   (Slow)   |     (Slow)    |    (Slow)   |      -      |     -      |     -      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   Non ² Textures                |      -      |      -      |     -      |     X      |       X       |      X      |      -      |     -      |     -      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glVertexPointer()       (PTR) |      X      |      X      |     X      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glVertexPointer()       (VBO) |      -      |      X      |     X      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glBufferData()                |      -      |      X      |     X      |     X      |       X       |      X      |      X      |     X      |     X      |     X
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glMapBuffer()                 |      -      |      X      |   OES x    |     X      |       X       |      X      |    OES x    |     -      |    OES x   |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glBindFramebuffer()           |      -      |      -      |   OES x    |     -      |       X       |      X      |      X      |     X      |     X      |     X
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glVertexAttribPointer() (PTR) |      -      |      -      |     -      |     X      |       X       |      X      |      X      |     -      |     X      |     -
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   glVertexAttribPointer() (VBO) |      -      |      -      |     -      |     X      |       X       |      X      |      X      |     X      |     X      |     X
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   GLSL Version                  |      -      |      -      |     -      |    1.10    |     1.30      |    1.30     |     1.00    |    1.00    |    3.00    |    3.00
+   ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   bool legacy          :1; //   |      X      |      X      |     -      |     X      |       X       |      -      |      -      |     -      |     -      |     -
+   bool shaders         :1; //   |      -      |      -      |     -      |     X      |       X       |      X      |      X      |     X      |     X      |     X
+   bool nonPow2Textures :1; //   |      -      |      -      |     -      |     X      |       X       |      X      |      -      |     -      |     -      |     -
+   bool vertexBuffer    :1; //   |      -      |      X      |     X      |     X      |       X       |      X      |      X      |     X      |     X      |     X
+   bool frameBuffer     :1; //   |      -      |      -      |     ~      |     -      |       X       |      X      |      X      |     X      |     X      |     X
+// bool mapBuffer       :1; //   |      -      |      X      |     ~      |     X      |       X       |      X      |      ~      |     -      |     ~      |     -
+*/
 
-#endif
+default:
+// Capabilities Global set to capabilities of Display being rendered to
+GLCapabilities glCaps;
+// Requiring Graphics Reload:
+bool glCaps_nonPow2Textures, glCaps_vertexBuffer, glCaps_quads, glCaps_intAndDouble, glCaps_legacyFormats, glCaps_compatible, glCaps_vertexPointer;
+// Might toggle without Reload:
+bool glCaps_core, glCaps_shaders, glCaps_fixedFunction, glCaps_immediate, glCaps_legacy, glCaps_pointSize, glCaps_frameBuffer, glCaps_vao, glCaps_select;
+// bool mapBuffer;
+private:
 
-#define glLoadMatrix glLoadMatrixd
-#define glMultMatrix glMultMatrixd
-#define glGetMatrix  glGetDoublev
-#define glTranslate glTranslated
-#define glScale glScaled
 
+// **********   Errors and Debugging   **********
 /*
-#define glVertex3v glVertex3dv
-#define glNormal3v glNormal3dv
+void CheckGLErrors()
+{
+   int e, nCount = 0;
+   while((e = glGetError()) && nCount++ < 10)
+      printf("GL error %d!\n", e);
+}
 */
-
-/*
-//#ifdef VERTEX_FORMAT_DOUBLE
-
-#define glLoadMatrix glLoadMatrixd
-#define glMultMatrix glMultMatrixd
-#define glGetMatrix  glGetDoublev
-#define glVertex3v glVertex3dv
-#define glNormal3v glNormal3dv
-#define glTranslate glTranslated
-#define glScale glScaled
-//#define GL_VERTEX_FORMAT   GL_DOUBLE
-
-#else
-
-#define glLoadMatrix glLoadMatrixf
-#define glMultMatrix glMultMatrixf
-#define glGetMatrix  glGetFloatv
-#define glVertex3v glVertex3fv
-#define glNormal3v glNormal3fv
-#define glTranslate glTranslatef
-#define glScale glScalef
-//#define GL_VERTEX_FORMAT   GL_FLOAT
-
+#ifdef GL_DEBUGGING
+#ifndef APIENTRY
+   #define APIENTRY
 #endif
-*/
-
-#define GL_ARRAY_BUFFER_ARB            0x8892
-#define GL_ELEMENT_ARRAY_BUFFER_ARB    0x8893
-#define GL_STATIC_DRAW_ARB             0x88E4
-#define GL_LIGHT_MODEL_COLOR_CONTROL   0x81F8
-#define GL_SEPARATE_SPECULAR_COLOR     0x81FA
-
-#define GL_MULTISAMPLE_ARB             0x809D
-
-#if defined(__WIN32__)
-   #include "wglDefs.h"
-
-   typedef void (APIENTRY * PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);
-   typedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void);
+static void APIENTRY openglCallbackFunction(GLenum source,
+                                           GLenum type,
+                                           GLuint id,
+                                           GLenum severity,
+                                           GLsizei length,
+                                           const GLchar* message,
+                                           const void* userParam)
+{
+   if(severity == GL_DEBUG_SEVERITY_NOTIFICATION)
+      return;
+   PrintLn("---------------------opengl-callback-start------------");
+   PrintLn("message: ", message);
+   PrintLn("type: ");
+   switch (type)
+   {
+      case GL_DEBUG_TYPE_ERROR: PrintLn("ERROR"); break;
+      case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: PrintLn("DEPRECATED_BEHAVIOR"); break;
+      case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: PrintLn("UNDEFINED_BEHAVIOR"); break;
+      case GL_DEBUG_TYPE_PORTABILITY: PrintLn("PORTABILITY"); break;
+      case GL_DEBUG_TYPE_PERFORMANCE: PrintLn("PERFORMANCE"); break;
+      case GL_DEBUG_TYPE_OTHER: PrintLn("OTHER"); break;
+   }
 
-   static PFNGLLOCKARRAYSEXTPROC glLockArraysEXT = null;
-   static PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT = null;
+   PrintLn("id: ", id);
+   Print("severity: ");
+   switch (severity)
+   {
+      case GL_DEBUG_SEVERITY_LOW: PrintLn("LOW"); break;
+      case GL_DEBUG_SEVERITY_MEDIUM: PrintLn("MEDIUM"); break;
+      case GL_DEBUG_SEVERITY_HIGH: PrintLn("HIGH"); break;
+      default: PrintLn("(other)");
+   }
+   PrintLn("---------------------opengl-callback-end--------------");
+}
 
-   static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = null;
-   static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = null;
-   static PFNWGLCREATEPBUFFERARBPROC wglCreatePbufferARB = null;
-   static PFNWGLGETPBUFFERDCARBPROC wglGetPbufferDCARB = null;
-   static PFNWGLQUERYPBUFFERARBPROC wglQueryPbufferARB = null;
-   static PFNWGLDESTROYPBUFFERARBPROC wglDestroyPbufferARB = null;
-   static PFNWGLRELEASEPBUFFERDCARBPROC wglReleasePbufferDCARB = null;
-   static PFNWGLBINDTEXIMAGEARBPROC wglBindTexImageARB = null;
-   static PFNWGLRELEASETEXIMAGEARBPROC wglReleaseTexImageARB = null;
-   static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = null;
-   static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = null;
+static void setupDebugging()
+{
+   if(glDebugMessageCallback)
+   {
+      GLuint unusedIds = 0;
 
-#elif defined(__ANDROID__) || defined(__ODROID__)
+      glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
 
-   #define GL_FRAMEBUFFER           GL_FRAMEBUFFER_OES
-   #define GL_RENDERBUFFER          GL_RENDERBUFFER_OES
-   #define GL_COLOR_ATTACHMENT0     GL_COLOR_ATTACHMENT0_OES
-
-   #define GL_POLYGON_STIPPLE 0xFFFF
-   #define GL_LINE_STIPPLE 0xFFFF
-   #define GL_LINE 0xFFFF
-   #define GL_FILL 0xFFFF
-   #define GL_ALL_ATTRIB_BITS 0xFFFF
-   #define GL_LIGHT_MODEL_LOCAL_VIEWER 0xFFFF
-
-   #define GL_POLYGON      9
-   #define GL_QUADS        7
-
-   //#define GL_QUADS              0
-   #define GL_QUAD_STRIP         0
-   //#define GL_DOUBLE             0
-   //#define GL_UNSIGNED_INT       0
-   //#define GL_FILL               0
-   //#define GL_LINE               0
-   //#define GL_LINE_STIPPLE       0
-   #define GL_BGRA_EXT           0
-   #define GL_UNPACK_ROW_LENGTH  0
-   #define GL_UNPACK_SKIP_PIXELS 0
-   #define GL_UNPACK_SKIP_ROWS   0
-   #define GL_RGBA8              0
-   #define GL_PACK_ROW_LENGTH    0
-   #define GL_PACK_SKIP_ROWS     0
-   #define GL_PACK_SKIP_PIXELS   0
-
-#endif
-
-
-#if defined(__ANDROID__) || defined(__ODROID__)
-   #define glBindFramebuffer        glBindFramebufferOES
-   #define glBindRenderbuffer       glBindRenderbufferOES
-   #define glFramebufferTexture2D   glFramebufferTexture2DOES
-   #define glGenFramebuffers        glGenFramebuffersOES
-   #define glGenRenderbuffers       glGenRenderbuffersOES
-   #define glDeleteFramebuffers     glDeleteFramebuffersOES
-   #define glDeleteRenderbuffers    glDeleteRenderbuffersOES
-
-   #define GL_INT                                  0x1404
-   #define GL_UNSIGNED_INT                         0x1405
-   #define GL_DOUBLE                               0x140A
-   #define APIENTRY
+      glDebugMessageCallback(openglCallbackFunction, null);
+      glDebugMessageControl(GL_DONT_CARE,
+          GL_DONT_CARE,
+          GL_DONT_CARE,
+          0,
+          &unusedIds,
+          GL_TRUE);
+   }
+}
 #endif
 
-#if defined(ES1_1) || defined(SHADERS)
-
-   #undef glRecti
-   #undef glBegin
-   #undef glTexCoord2i
-   #undef glVertex2i
-   #undef glTexCoord2d
-   #undef glVertex2d
-   #undef glTexCoord2f
-   #undef glVertex2f
-   #undef glEnd
-   #undef glColor3f
-   #undef glColor4ub
-   #undef glColor4fv
-   #undef glNormal3fv
-   #undef glNormal3f
-   #undef glTexCoord2fv
-   #undef glVertex3d
-   #undef glVertex3dv
-   #undef glVertex3f
-   #undef glVertex3fv
-
-   #undef glLoadMatrixd
-   #undef glMultMatrixd
-   #undef glFrustum
-   #undef glOrtho
-   #undef glScaled
-   #undef glScalef
-   #undef glTranslated
-   #undef glRotated
-   #undef glMatrixMode
-   #undef glLoadIdentity
-   #undef glPushMatrix
-   #undef glPopMatrix
-
-   #undef glLineStipple
-   #undef glColorMaterial
-   #undef glLightModeli
-
-   #define glRecti               glimtkRecti
-   #define glBegin               glimtkBegin
-   #define glTexCoord2i          glimtkTexCoord2i
-   #define glVertex2i            glimtkVertex2i
-   #define glTexCoord2d          glimtkTexCoord2d
-   #define glVertex2d            glimtkVertex2d
-   #define glTexCoord2f          glimtkTexCoord2f
-   #define glVertex2f            glimtkVertex2f
-   #define glEnd                 glimtkEnd
-   #define glColor3f             glimtkColor3f
-   #define glColor4ub            glimtkColor4ub
-   #define glColor4fv            glimtkColor4fv
-   #define glNormal3fv           glimtkNormal3fv
-   #define glNormal3f            glimtkNormal3f
-   #define glTexCoord2fv         glimtkTexCoord2fv
-   #define glVertex3d            glimtkVertex3d
-   #define glVertex3dv           glimtkVertex3dv
-   #define glVertex3f            glimtkVertex3f
-   #define glVertex3fv           glimtkVertex3fv
-
-   #define glLoadMatrixd         glmsLoadMatrixd
-   #define glMultMatrixd         glmsMultMatrixd
-   #define glFrustum             glmsFrustum
-   #define glOrtho               glmsOrtho
-   #define glScaled              glmsScaled
-   #define glScalef              glmsScaled
-   #define glTranslated          glmsTranslated
-   #define glRotated             glmsRotated
-   #define glMatrixMode          glmsMatrixMode
-   #define glLoadIdentity        glmsLoadIdentity
-   #define glPushMatrix          glmsPushMatrix
-   #define glPopMatrix           glmsPopMatrix
-
-   #define glLineStipple         glesLineStipple
-   #define glColorMaterial       glesColorMaterial
-   #define glLightModeli         glesLightModeli
-
-#endif
-
-public void glesColorMaterial(int a, int b)
-{
-   PrintLn("glColorMaterial stub");
-}
 
 static GLuint stippleTexture;
-#if defined(ES1_1) || defined(SHADERS) || defined(EM_MODE)
 static bool stippleEnabled;
-#endif
 
-public void glesLineStipple( int i, unsigned short j )
+public void glsupLineStipple( int i, uint16 j )
 {
    uint texture[1*16];
    int x;
@@ -406,91 +262,125 @@ public void glesLineStipple( int i, unsigned short j )
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-   glMatrixMode(GL_TEXTURE);
-   glLoadIdentity();
+   GLMatrixMode(GL_TEXTURE);
+   GLLoadIdentity();
    //glTranslated(1.0/backAttrib->texW/2.0f, 1.0/backAttrib->texH/2.0f, 0.0f);
-   glScaled(i/16.0, 1, 1.0f);
-   glTranslated(0.5, 0.5, 0);
-   glMatrixMode(MatrixMode::projection);
+   GLScaled(i/16.0, 1, 1.0f);
+   GLTranslated(0.5, 0.5, 0);
+   GLMatrixMode(MatrixMode::projection);
 }
 
-public void glesLightModeli( unsigned int pname, int param )
-{
-#if !defined(EM_MODE) && !defined(SHADERS)
-   if(pname == GL_LIGHT_MODEL_TWO_SIDE)
-      glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, param);
+   // Exported to build _GLES version...
+   public void glsupLightModeli( unsigned int pname, int param )
+   {
+#if ENABLE_GL_FFP
+      if(pname == GL_LIGHT_MODEL_TWO_SIDE)
+         glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, param);
 #endif
-}
+   }
 
-#if defined(__ANDROID__) || defined(__ODROID__)
+#ifdef _GLES
+   void glFogi( unsigned int pname, int param ) { }
+   void glPolygonMode( unsigned int i, unsigned int j ) { }
+   void glBlendFuncSeparate(int a, int b, int c, int d)
+   {
+      glBlendFunc(a, b);
+   }
+
+#endif
+
+#if defined(_GLES) || defined(_GLES2)
 void glClearDepth( double depth ) { glClearDepthf((float)depth); }
-void glFogi( unsigned int pname, int param ) { }
-void glPolygonMode( unsigned int i, unsigned int j ) { }
+#endif
 
+#if !ENABLE_GL_SELECT
 
 // *** Picking won't be supported for now ***
 void glPushName( unsigned int i ) { }
 void glLoadName( unsigned int i ) { }
 void glPopName() { }
 
-// Probably replace by regular glBlendFunc ...
-void glBlendFuncSeparate(int a, int b, int c, int d)
+#endif
+
+#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
+static inline uint getPrimitiveType(RenderPrimitiveType type)
 {
-   glBlendFunc(a, b);
+   static int primitiveTypes[RenderPrimitiveType] =
+   {
+      GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN,
+      GLIMTKMode::quads,
+      GLIMTKMode::quadStrip,
+      GL_LINE_STRIP
+   };
+   // NOTE: This will only work for single quads
+   return (type == quads && !glCaps_quads) ? GL_TRIANGLE_FAN : primitiveTypes[type];
 }
 
-// For direct pixel blitting...
-void glRasterPos2d(double a, double b) { }
-void glPixelZoom(float a, float b) { }
-void glDrawPixels(int a, int b, int c, int d, void * e) { }
+public void GLSetupTexturing(bool enable)
+{
+#if ENABLE_GL_SHADERS
+   if(glCaps_shaders)
+      defaultShader.texturing(enable);
+#endif
 
+#if ENABLE_GL_FFP
+   if(!glCaps_shaders)
+      (enable ? glEnable : glDisable)(GL_TEXTURE_2D);
 #endif
+}
 
-#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
-static int primitiveTypes[RenderPrimitiveType] =
+public void GLSetupFog(bool enable)
 {
-   GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GLIMTKMode::quads, GLIMTKMode::quadStrip, GL_LINE_STRIP
-};
+#if ENABLE_GL_SHADERS
+   if(glCaps_shaders)
+      defaultShader.fog(enable);
 #endif
 
-public void GLSetupTexturing(bool enable)
-{
-#ifdef SHADERS
-   shader_texturing(enable);
-#else
-   (enable ? glEnable : glDisable)(GL_TEXTURE_2D);
+#if ENABLE_GL_FFP
+   if(!glCaps_shaders)
+      (enable ? glEnable : glDisable)(GL_FOG);
 #endif
 }
 
+bool lightingEnabled;
 
-// Non OpenGL ES friendly stuff
+public void GLSetupLighting(bool enable)
+{
+   lightingEnabled = enable;
+#if ENABLE_GL_SHADERS
+   if(glCaps_shaders)
+      defaultShader.lighting(enable);
+#endif
 
-#if defined(ES1_1)
+#if ENABLE_GL_FFP
+   if(!glCaps_shaders)
+      (enable ? glEnable : glDisable)(GL_LIGHTING);
+#endif
+}
+#endif
 
-//#undef GL_UNSIGNED_INT
-//#undef GL_DOUBLE
-#undef GL_INT
-//#undef GL_POLYGON
-//#undef GL_QUADS
-#undef GL_QUAD_STRIP
-#undef GL_POLYGON_STIPPLE
-#undef GL_LINE_STIPPLE
-#undef GL_LINE
-#undef GL_FILL
-#undef GL_ALL_ATTRIB_BITS
-#undef GL_LIGHT_MODEL_LOCAL_VIEWER
+/*static */GLuint lastBlitTex;
 
-#endif
+Shader activeShader;
 
 static int displayWidth, displayHeight;
 
 #define GL_CLAMP_TO_EDGE 0x812F
 
-static bool vboAvailable;
-
 static bool useSingleGLContext = false;
 class OGLDisplay : struct
 {
+   GLCapabilities capabilities, originalCapabilities;
+   bool compat;
+   int version;
+
+   ColorAlpha * flippingBuffer;
+   int flipBufH, flipBufW;
+   bool depthWrite;
+   int x, y;
+   uint vao;
+   int maxTMU;
+
 #if defined(__WIN32__)
    HDC hdc;
    HGLRC glrc;
@@ -520,63 +410,32 @@ class OGLDisplay : struct
    Pixmap shapePixmap;
    X11Picture shapePicture;
 #endif
-
-   ColorAlpha * flippingBuffer;
-   int flipBufH, flipBufW;
-   bool depthWrite;
-   int x, y;
 };
 
-#ifdef _DEBUG
-static void APIENTRY openglCallbackFunction(GLenum source,
-                                           GLenum type,
-                                           GLuint id,
-                                           GLenum severity,
-                                           GLsizei length,
-                                           const GLchar* message,
-                                           const void* userParam)
-{
-   PrintLn("---------------------opengl-callback-start------------");
-   PrintLn("message: ", message);
-   PrintLn("type: ");
-   switch (type)
-   {
-      case GL_DEBUG_TYPE_ERROR: PrintLn("ERROR"); break;
-      case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: PrintLn("DEPRECATED_BEHAVIOR"); break;
-      case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: PrintLn("UNDEFINED_BEHAVIOR"); break;
-      case GL_DEBUG_TYPE_PORTABILITY: PrintLn("PORTABILITY"); break;
-      case GL_DEBUG_TYPE_PERFORMANCE: PrintLn("PERFORMANCE"); break;
-      case GL_DEBUG_TYPE_OTHER: PrintLn("OTHER"); break;
-   }
-
-   PrintLn("id: ", id);
-   Print("severity: ");
-   switch (severity)
-   {
-      case GL_DEBUG_SEVERITY_LOW: PrintLn("LOW"); break;
-      case GL_DEBUG_SEVERITY_MEDIUM: PrintLn("MEDIUM"); break;
-      case GL_DEBUG_SEVERITY_HIGH: PrintLn("HIGH"); break;
-   }
-   PrintLn("---------------------opengl-callback-end--------------");
-}
-#endif
-
 class OGLSystem : struct
 {
    int maxTextureSize;
    bool loadingFont;
-   bool pow2textures;
 #if defined(__WIN32__)
    PIXELFORMATDESCRIPTOR pfd;
    int format;
    HDC hdc;
    HGLRC glrc;
    HWND hwnd;
-#elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+#elif defined(__EMSCRIPTEN__)
+   EMSCRIPTEN_WEBGL_CONTEXT_HANDLE glc;
+#elif !defined(__ANDROID__) && !defined(__ODROID__)
    XVisualInfo * visualInfo;
    GLXContext glContext;
    GLXDrawable glxDrawable;
 #endif
+   GLCapabilities capabilities;
+   bool compat;
+   int version;
+
+   // Buffer Data
+   uint16 *shortBDBuffer;
+   uint shortBDSize;
 };
 
 class OGLSurface : struct
@@ -594,6 +453,8 @@ class OGLMesh : struct
 {
    GLAB vertices;
    GLAB normals;
+   GLAB tangents;
+   GLAB lightVectors;
    GLAB texCoords;
    GLAB texCoords2;
    GLAB colors;
@@ -609,42 +470,59 @@ class OGLIndices : struct
 int current;
 void * previous;
 
-static void setupDebugging()
-{
-#ifdef _DEBUG
-   if(glDebugMessageCallback)
-   {
-      GLuint unusedIds = 0;
-
-      glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
-      glDebugMessageCallback(openglCallbackFunction, null);
-      glDebugMessageControl(GL_DONT_CARE,
-          GL_DONT_CARE,
-          GL_DONT_CARE,
-          0,
-          &unusedIds,
-          GL_TRUE);
-   }
-#endif
-}
-
 #if defined(__WIN32__)
-static HGLRC winCreateContext(HDC hdc)
+static HGLRC winCreateContext(HDC hdc, int * contextVersion, bool * isCompatible, bool compatible)
 {
+   HGLRC result = 0;
    if(wglCreateContextAttribsARB)
    {
-      int attribs[] =
+      int versions[12][2] =
       {
-         WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
-         WGL_CONTEXT_MINOR_VERSION_ARB, 4,
-         WGL_CONTEXT_FLAGS_ARB, /*WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | */WGL_CONTEXT_DEBUG_BIT_ARB,
-         WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB /*WGL_CONTEXT_CORE_PROFILE_BIT_ARB*/,
-         0,0
+         { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 },
+                             { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 },
+                                                 { 2, 1 }, { 2, 0 }
       };
-      return wglCreateContextAttribsARB(hdc, null, attribs);
+
+      bool tryingCompat = compatible;
+      int v = 0;
+      while(!result)
+      {
+         for(v = 0; !result && v < sizeof(versions) / sizeof(versions[0]); v++)
+         {
+            int v0 = versions[v][0], v1 = versions[v][1];
+            if(!tryingCompat || v0 >= 3)
+            {
+               bool coreNotion = v0 > 3 || (v0 == 3 && v1 >= 3);
+               int attribs[] =
+               {
+                  WGL_CONTEXT_MAJOR_VERSION_ARB, v0, WGL_CONTEXT_MINOR_VERSION_ARB, v1,
+         #ifdef _DEBUG
+                  WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
+         #endif
+                  coreNotion ? WGL_CONTEXT_PROFILE_MASK_ARB : 0, coreNotion ? (tryingCompat ? WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB : WGL_CONTEXT_CORE_PROFILE_BIT_ARB) : 0,
+                  0,0
+               };
+               result = wglCreateContextAttribsARB(hdc, null, attribs);
+               if(result)
+               {
+                  if(contextVersion) *contextVersion = v0;
+                  if(isCompatible)   *isCompatible = tryingCompat || !coreNotion;
+               }
+            }
+         }
+         if(tryingCompat)
+            tryingCompat = false;
+         else
+            break;
+      }
    }
-   else
-      return wglCreateContext(hdc);
+   if(!result)
+   {
+      if(contextVersion) *contextVersion = 1;
+      if(isCompatible)   *isCompatible = true;
+      result = wglCreateContext(hdc);
+   }
+   return result;
 }
 #endif
 
@@ -654,7 +532,10 @@ class OpenGLDisplayDriver : DisplayDriver
 
    bool LockSystem(DisplaySystem displaySystem)
    {
-#if !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+#if defined(__EMSCRIPTEN__)
+      OGLSystem oglSystem = displaySystem.driverData;
+      emscripten_webgl_make_context_current(oglSystem.glc);
+#elif !defined(__ANDROID__) && !defined(__ODROID__)
       OGLSystem oglSystem = displaySystem.driverData;
       if(useSingleGLContext) return true;
    #if defined(__WIN32__)
@@ -779,19 +660,83 @@ class OpenGLDisplayDriver : DisplayDriver
       }
    }
 
-   void ::CheckExtensions(OGLSystem oglSystem)
+#if !defined(__EMSCRIPTEN__)
+   void ::CheckCapabilities(OGLSystem oglSystem, OGLDisplay oglDisplay, bool canCheckExtensions)
    {
-      const char * extensions = (const char *)glGetString(GL_EXTENSIONS);
-      if(extensions)
-         oglSystem.pow2textures = strstr(extensions, "GL_ARB_texture_non_power_of_two") ? false : true;
+      GLCapabilities capabilities;
+#if !defined(_GLES2)
+      const char * extensions = (canCheckExtensions && (!oglDisplay || oglDisplay.compat)) ? (const char *)glGetString(GL_EXTENSIONS) : null;
+#endif
+#ifdef DIAGNOSTICS
+      printf("extensions: %s\n", extensions);
+#endif
+
       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &oglSystem.maxTextureSize);
+
+#if defined(_GLES)
+      capabilities = { fixedFunction = true, vertexPointer = true, vertexBuffer = true, pointSize = true, legacyFormats = true, frameBuffer = extensions && strstr(extensions, "GL_OES_framebuffer_object") };
+#elif defined(_GLES2)
+      capabilities = { glCaps_shaders = true, vertexBuffer = true, pointSize = true, frameBuffer = true, legacyFormats = true };
+#else
+      capabilities =
+      {
+         nonPow2Textures = glGetStringi || (extensions && (strstr(extensions, "GL_ARB_texture_non_power_of_two")));
+         intAndDouble = true;
+#ifdef GL_DEBUGGING
+         debug = true;
+#endif
+         compatible = oglDisplay.compat;
+         pointSize = oglDisplay.compat;
+#if ENABLE_GL_LEGACY
+         legacy         = glBegin != null && oglDisplay.compat;
+         legacyFormats  = glBegin != null && oglDisplay.compat;
+         immediate      = glBegin != null && oglDisplay.compat;
+         fixedFunction  = glBegin != null && oglDisplay.compat;
+         quads          = glBegin != null && oglDisplay.compat;
+         select         = glSelectBuffer != null && oglDisplay.compat;
+#endif
+#if ENABLE_GL_SHADERS
+         shaders = glCreateProgram != null;
+#endif
+#if ENABLE_GL_POINTER
+         vertexPointer = oglDisplay.compat;
+#endif
+#if ENABLE_GL_VAO
+         vao = glBindVertexArray != null && !oglDisplay.compat;
+#endif
+#if ENABLE_GL_FBO
+         frameBuffer = glBindFramebuffer != null;
+#endif
+         vertexBuffer = glBindBuffer != null;
+         // mapBuffer = glMapBuffer != null;
+      };
+#endif
+
+#ifdef DIAGNOSTICS
+      PrintLn("max texture size: ", oglSystem.maxTextureSize);
+#endif
+      if(oglDisplay) oglDisplay.capabilities = capabilities;
+      if(oglSystem)  oglSystem.capabilities = capabilities;
    }
+#endif
 
    bool CreateDisplaySystem(DisplaySystem displaySystem)
    {
       bool result = false;
       OGLSystem oglSystem = displaySystem.driverData = OGLSystem { };
 
+#ifdef _GLES
+      oglSystem.capabilities = { fixedFunction = true, vertexBuffer = true, frameBuffer = true, pointSize = true };
+#elif defined(_GLES2)
+      oglSystem.capabilities = { shaders = true, vertexBuffer = true, frameBuffer = true, pointSize = true };
+#else
+      oglSystem.capabilities = { compatible = glCaps_compatible, shaders = true, fixedFunction = true, immediate = true, legacy = true, pointSize = true, quads = true, intAndDouble = true, vertexBuffer = true, frameBuffer = true, vao = true, nonPow2Textures = true };
+#endif
+
+#ifdef DIAGNOSTICS
+      PrintLn("OpenGL driver's CreateDisplaySystem()");
+#endif
+
    #ifdef __WIN32__
       oglSystem.hwnd = CreateWindow("static", null, 0,0,0,0,0,null,null,null,null);
 
@@ -884,8 +829,15 @@ class OpenGLDisplayDriver : DisplayDriver
                      SetPixelFormat(oglSystem.hdc, oglSystem.format, &oglSystem.pfd);
                      //Log("Successfully set pixel format\n");
 
-                     oglSystem.glrc = winCreateContext(oglSystem.hdc);
-                     wglMakeCurrent(oglSystem.hdc, oglSystem.glrc);
+#ifdef DIAGNOSTICS
+                     PrintLn("winCreateContext()");
+#endif
+                     oglSystem.glrc = winCreateContext(oglSystem.hdc, &oglSystem.version, &oglSystem.compat, displaySystem.glCapabilities.compatible);
+#ifdef DIAGNOSTICS
+                     PrintLn("wglMakeCurrent()");
+#endif
+                     if(oglSystem.glrc)
+                        wglMakeCurrent(oglSystem.hdc, oglSystem.glrc);
                   }
                }
                /*else
@@ -900,27 +852,86 @@ class OpenGLDisplayDriver : DisplayDriver
          }
       }
    #elif defined(__unix__) || defined(__APPLE__)
-      vboAvailable = true;
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__)
+         #if defined(__ANDROID__)
          egl_init_display(guiApp.desktop.windowHandle);
-      #elif defined(__ODROID__)
+         #elif defined(__ODROID__)
          egl_init_display((uint)displaySystem.window);
-         CheckExtensions(oglSystem);
+         #endif
+         CheckCapabilities(oglSystem, null, true);
+
+         // TODO: Clean this up? Needed here?
+         GLEnableClientState(VERTICES);
+         /*
+         // Initialize GL state.
+         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+         glEnable(GL_CULL_FACE);
+         glShadeModel(GL_SMOOTH);
+         glDisable(GL_DEPTH_TEST);
+         */
+         glDisable(GL_CULL_FACE);
+         glDisable(GL_DEPTH_TEST);
+
+         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+         glEnable(GL_BLEND);
+
+         matrixStack[0][0].Identity();
+         matrixStack[1][0].Identity();
+         matrixStack[2][0].Identity();
+
+         GLMatrixMode(GL_MODELVIEW);
+         GLScaled(1.0, 1.0, -1.0);
+         GLMatrixMode(GL_PROJECTION);
+         glShadeModel(GL_FLAT);
+
+#if !defined(_GLES)
+         if(!glCaps_shaders)
+            ;//GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
+#endif
+         glFogi(GL_FOG_MODE, GL_EXP);
+         glFogf(GL_FOG_DENSITY, 0);
+         glEnable(GL_NORMALIZE);
+         glDepthFunc(GL_LESS);
+         glClearDepth(1.0);
+         glDisable(GL_MULTISAMPLE);
+
+         glViewport(0,0,eglWidth,eglHeight);
+         GLLoadIdentity();
+         GLOrtho(0,eglWidth,eglHeight,0,0.0,1.0);
+
+         glabCurArrayBuffer = 0;
+         glabCurElementBuffer = 0;
+
          result = true;
       #elif defined(__EMSCRIPTEN__)
-         if(glfwInit() == GL_TRUE)
          {
-            const int width = 640, height = 480;
-            if(glfwOpenWindow(width, height, 8, 8, 8, 8, 16, 0, GLFW_WINDOW) == GL_TRUE)
-            {
-               //glfwSwapBuffers();
+            EmscriptenWebGLContextAttributes attribs = { 0 };
+            attribs.depth = 1;
+            attribs.antialias = 1;
+
+            /*
+              EM_BOOL alpha;
+              EM_BOOL depth;
+              EM_BOOL stencil;
+              EM_BOOL antialias;
+              EM_BOOL premultipliedAlpha;
+              EM_BOOL preserveDrawingBuffer;
+              EM_BOOL preferLowPowerToHighPerformance;
+              EM_BOOL failIfMajorPerformanceCaveat;
+              int majorVersion;
+              int minorVersion;
+              EM_BOOL enableExtensionsByDefault;
+              */
+
+            emscripten_webgl_init_context_attributes(&attribs);
+            oglSystem.maxTextureSize = 16384;
+            oglSystem.glc = emscripten_webgl_create_context("canvas", &attribs);
+            if(emscripten_webgl_make_context_current(oglSystem.glc) == EMSCRIPTEN_RESULT_SUCCESS)
                result = true;
-            }
-            else
-               printf("glfwOpenWindow() failed\n"); //glfwTerminate();
+
+            /*glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+            glEnable(GL_BLEND);*/
          }
-         else
-            printf("glfwInit() failed\n"); //glfwTerminate();
       #else
       {
          X11Window root = RootWindow( xGlobalDisplay, DefaultScreen( xGlobalDisplay ) );
@@ -953,8 +964,6 @@ class OpenGLDisplayDriver : DisplayDriver
          if(oglSystem.glContext)
          {
             glXMakeCurrent(xGlobalDisplay, oglSystem.glxDrawable, oglSystem.glContext);
-            // Setup Extensions
-            CheckExtensions(oglSystem);
             glXMakeCurrent(xGlobalDisplay, None, null);
             result = true;
          }
@@ -971,6 +980,19 @@ class OpenGLDisplayDriver : DisplayDriver
    void DestroyDisplaySystem(DisplaySystem displaySystem)
    {
       OGLSystem oglSystem = displaySystem.driverData;
+      if(stippleTexture)
+      {
+         glDeleteTextures(1, &stippleTexture);
+         stippleTexture = 0;
+      }
+
+#if ENABLE_GL_SHADERS
+      defaultShader.free();
+      activeShader = null;
+#endif
+
+      delete oglSystem.shortBDBuffer;
+      glimtkTerminate();
 
    #if defined(__WIN32__)
       wglMakeCurrent( null, null );
@@ -986,7 +1008,7 @@ class OpenGLDisplayDriver : DisplayDriver
       #if defined(__ANDROID__) || defined(__ODROID__)
          egl_term_display();
       #elif defined(__EMSCRIPTEN__)
-         glfwTerminate();
+         emscripten_webgl_destroy_context(oglSystem.glc);
       #else
       if(oglSystem.visualInfo)
       {
@@ -996,6 +1018,8 @@ class OpenGLDisplayDriver : DisplayDriver
          XFree(oglSystem.visualInfo);
    #endif
       }
+      if(oglSystem.glContext)
+        glXDestroyContext(xGlobalDisplay, oglSystem.glContext);
 
       if(oglSystem.glxDrawable)
       {
@@ -1007,13 +1031,116 @@ class OpenGLDisplayDriver : DisplayDriver
       delete oglSystem;
    }
 
-   static bool ::initialDisplaySetup(Display display)
+   /*static */bool ::initialDisplaySetup(Display display, bool canCheckExtensions, bool loadExtensions)
    {
+      OGLDisplay oglDisplay = display.driverData;
+      OGLSystem oglSystem = display.displaySystem.driverData;
       bool result = true;
-      #ifdef SHADERS
-      loadShaders("<:ecere>shaders/fixed.vertex", "<:ecere>shaders/fixed.frag");
-      #endif
-      glEnableClientState(GL_VERTEX_ARRAY);
+
+#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
+      if(loadExtensions && ogl_LoadFunctions() == ogl_LOAD_FAILED)
+         PrintLn("ogl_LoadFunctions() failed!");
+      CheckCapabilities(oglSystem, oglDisplay, canCheckExtensions);
+#endif
+
+      {
+         GLCapabilities capabilities = *&display.glCapabilities;
+         // PrintLn("Available OpenGL Capabilities: ", oglDisplay.capabilities);
+         // PrintLn("Desired OpenGL Capabilities: ", capabilities);
+
+         oglDisplay.originalCapabilities = oglDisplay.capabilities;
+
+         // Re-enable glCaps_shaders if no fixed function support
+         if(!oglDisplay.capabilities.fixedFunction)
+            capabilities.shaders = true;
+         // Re-enable fixed function if no glCaps_shaders support
+         if(!oglDisplay.capabilities.shaders)
+         {
+            capabilities.fixedFunction = true;
+            capabilities.shaders = false;
+         }
+
+         if(!capabilities.shaders && !capabilities.fixedFunction)
+         {
+            capabilities.fixedFunction = oglDisplay.capabilities.fixedFunction;
+            capabilities.shaders = oglDisplay.capabilities.shaders;
+         }
+
+         // Disable things that don't work with glCaps_shaders
+         if(capabilities.shaders)
+         {
+            capabilities.fixedFunction = false;
+            capabilities.legacy = false;
+            capabilities.immediate = false;
+         }
+
+         #if !ENABLE_GL_POINTER
+         // Re-enable vertex buffer if no pointer support
+         capabilities.vertexBuffer = true;
+         #endif
+
+         oglDisplay.capabilities &= capabilities;
+
+         // PrintLn("Selected OpenGL Capabilities: ", oglDisplay.capabilities);
+         oglSystem.capabilities = oglDisplay.capabilities;
+      }
+
+   #ifdef GL_DEBUGGING
+      if(oglDisplay.capabilities.debug)
+         setupDebugging();
+   #else
+      oglDisplay.capabilities.debug = false;
+   #endif
+
+#if ENABLE_GL_VAO
+      if(oglDisplay.capabilities.vao)
+      {
+         glGenVertexArrays(1, &oglDisplay.vao);
+         glBindVertexArray(oglDisplay.vao);
+      }
+#endif
+
+      oglSystem.capabilities = oglDisplay.capabilities;
+      SETCAPS(oglDisplay.capabilities);
+
+#if ENABLE_GL_SHADERS
+      if(glCaps_shaders)
+      {
+#if ENABLE_GL_LEGACY
+         if(oglDisplay.compat)
+         {
+            glDisableClientState(GL_VERTEX_ARRAY);
+            glDisableClientState(GL_NORMAL_ARRAY);
+            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+            glDisableClientState(GL_COLOR_ARRAY);
+         }
+#endif
+         defaultShader.select();
+      }
+#if ENABLE_GL_LEGACY
+      else
+      {
+         glDisableVertexAttribArray(GLBufferContents::color);
+         glDisableVertexAttribArray(GLBufferContents::normal);
+         glDisableVertexAttribArray(GLBufferContents::texCoord);
+         glDisableVertexAttribArray(GLBufferContents::vertex);
+         glDisableVertexAttribArray(GLBufferContents::tangent1);
+         glDisableVertexAttribArray(GLBufferContents::tangent2);
+#if ENABLE_GL_VAO
+         glBindVertexArray(0);
+#endif
+         glUseProgram(0);
+      }
+#endif
+
+#endif
+
+#if ENABLE_GL_VAO
+      if(glCaps_vao)
+         glBindVertexArray(oglDisplay.vao);
+#endif
+
+      GLEnableClientState(VERTICES);
 
       GLABBindBuffer(GL_ARRAY_BUFFER, 0);
       GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@@ -1028,24 +1155,41 @@ class OpenGLDisplayDriver : DisplayDriver
 #endif
       glEnable(GL_BLEND);
 
-      glMatrixMode(MatrixMode::modelView);
-      glLoadIdentity(); // For setting up GLES stack
-      glScaled(1.0, 1.0, -1.0);
+      GLMatrixMode(MatrixMode::texture);
+      GLLoadIdentity();
+
+      GLMatrixMode(MatrixMode::modelView);
+      GLLoadIdentity(); // For setting up GLES stack
+      GLScaled(1.0, 1.0, -1.0);
       // glTranslatef(0.375f, 0.375f, 0.0f);
       // glTranslatef(-0.625f, -0.625f, 0.0f);
-      glMatrixMode(MatrixMode::projection);
-#if !defined(EM_MODE) && !defined(SHADERS)
-      glShadeModel(GL_FLAT);
+      GLMatrixMode(MatrixMode::projection);
+      GLLoadIdentity();
+      if(display.width && display.height)
+         GLOrtho(0,display.width,display.height,0,0.0,1.0);
 
-      // glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, true);
-      glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
-      glFogi(GL_FOG_MODE, GL_EXP);
-      glFogf(GL_FOG_DENSITY, 0);
-      glEnable(GL_NORMALIZE);
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
+      {
+         glShadeModel(GL_FLAT);
+
+         #define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51
+         GLLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
+
+#if !defined(_GLES)
+         ;//GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
+#endif
+         glFogi(GL_FOG_MODE, GL_EXP);
+         glFogf(GL_FOG_DENSITY, 0);
+         glEnable(GL_NORMALIZE);
+      }
 #endif
       glDepthFunc(GL_LESS);
       glClearDepth(1.0);
-      glDisable(GL_MULTISAMPLE_ARB);
+#if !defined(__EMSCRIPTEN__)
+      glDisable(GL_MULTISAMPLE);
+#endif
+
 #if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
       display.ambient = Color { 50,50,50 };
 #endif
@@ -1056,22 +1200,20 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       bool result = false;
       OGLDisplay oglDisplay = display.driverData;
-#if !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
       OGLSystem oglSystem = display.displaySystem.driverData;
-#endif
 
       if(!oglDisplay)
          oglDisplay = display.driverData = OGLDisplay { };
-      //printf("Inside CreateDisplay\n");
+      oglDisplay.capabilities = oglSystem.capabilities;
 
 #if defined(__WIN32__) || defined(USEPBUFFER)
       if(!display.alphaBlend)
 #endif
       {
-   #if defined(__WIN32__)
+#if defined(__WIN32__)
          oglDisplay.hdc = GetDC(display.window);
          SetPixelFormat(oglDisplay.hdc, oglSystem.format, &oglSystem.pfd);
-         if((oglDisplay.glrc = winCreateContext(oglDisplay.hdc)))
+         if((oglDisplay.glrc = winCreateContext(oglDisplay.hdc, &oglDisplay.version, &oglDisplay.compat, (*&display.glCapabilities).compatible)))
          {
             wglShareLists(oglSystem.glrc, oglDisplay.glrc);
             wglMakeCurrent(oglDisplay.hdc, oglDisplay.glrc);
@@ -1079,9 +1221,10 @@ class OpenGLDisplayDriver : DisplayDriver
          }
          else
             ReleaseDC(display.window, oglDisplay.hdc);
-   #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
-      #else
+#elif defined(__unix__) || defined(__APPLE__)
+#  if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
+         result = true;
+#  else
          XVisualInfo * visualInfo = ((XWindowData)display.windowDriverData).visual;
          /*
 #if defined(__APPLE__)
@@ -1100,6 +1243,11 @@ class OpenGLDisplayDriver : DisplayDriver
          // visualInfo = oglSystem.visualInfo;
 //#endif
          */
+#if !defined(__APPLE__)
+         oglDisplay.compat = true;
+         oglDisplay.version = 4;
+#endif
+
          if(visualInfo)
          {
             //printf("visualInfo is not null\n");
@@ -1115,23 +1263,28 @@ class OpenGLDisplayDriver : DisplayDriver
             glXMakeCurrent(xGlobalDisplay, (GLXDrawable)display.window, oglDisplay.glContext);
             result = true;
          }
-      #endif
-   #endif
+#  endif
+#endif
       }
 #if defined(__WIN32__) || defined(USEPBUFFER)
       else
       {
+         oglDisplay.compat = (*&display.glCapabilities).compatible;
          result = true;
          wglMakeCurrent(oglSystem.hdc, oglSystem.glrc);
       }
 #endif
       if(result)
       {
-         ogl_LoadFunctions();
-         CheckExtensions(oglSystem);
-         vboAvailable = glBindBuffer != null;
-         setupDebugging();
-         initialDisplaySetup(display);
+#if defined(__EMSCRIPTEN__)
+         emscripten_webgl_make_context_current(oglSystem.glc);
+#endif
+
+#if defined(__WIN32__) || defined(USEPBUFFER)
+         initialDisplaySetup(display, !display.alphaBlend, true);
+#else
+         initialDisplaySetup(display, true, true);
+#endif
       }
 
       if(!useSingleGLContext)
@@ -1152,17 +1305,14 @@ class OpenGLDisplayDriver : DisplayDriver
          result = true;
       #endif
       }
-
       return result;
    }
 
    bool DisplaySize(Display display, int width, int height)
    {
       OGLDisplay oglDisplay = display.driverData;
-
       bool result = false;
 
-      //printf("Inside DisplaySize\n");
 #if defined(__WIN32__) || defined(USEPBUFFER)
       OGLSystem oglSystem = display.displaySystem.driverData;
       if(display.alphaBlend)
@@ -1246,7 +1396,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
          oglDisplay.pBuffer = wglCreatePbufferARB(oglSystem.hdc, pixelFormat, width, height, attributes);
          oglDisplay.hdc = wglGetPbufferDCARB(oglDisplay.pBuffer);
-         if((oglDisplay.glrc = winCreateContext(oglDisplay.hdc)))
+         if((oglDisplay.glrc = winCreateContext(oglDisplay.hdc, null, null, oglDisplay.capabilities.compatible)))
          {
             BITMAPINFO * info;
             HDC hdc = GetDC(display.window);
@@ -1486,9 +1636,11 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__)
          wglMakeCurrent(oglDisplay.hdc, oglDisplay.glrc);
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
+      #if defined(__ANDROID__) || defined(__ODROID__)
          width = eglWidth;
          height = eglHeight;
+      #elif defined(__EMSCRIPTEN__)
+         emscripten_webgl_make_context_current(oglSystem.glc);
       #else
          glXMakeCurrent(xGlobalDisplay, (GLXDrawable)display.window, oglDisplay.glContext);
       #endif
@@ -1498,8 +1650,10 @@ class OpenGLDisplayDriver : DisplayDriver
 #endif
          result = true;
 
+      SETCAPS(oglDisplay.capabilities);
+
       if(display.alphaBlend && result)
-         initialDisplaySetup(display);
+         initialDisplaySetup(display, true, false);
 
       if(!result && display.alphaBlend)
       {
@@ -1511,9 +1665,9 @@ class OpenGLDisplayDriver : DisplayDriver
       result = false;
 
       glViewport(0,0,width,height);
-      glMatrixMode(MatrixMode::projection);
-      glLoadIdentity();
-      glOrtho(0,width,height,0,0.0,1.0);
+      GLMatrixMode(MatrixMode::projection);
+      GLLoadIdentity();
+      GLOrtho(0,width,height,0,0.0,1.0);
       displayWidth = display.width = width;
       displayHeight = display.height = height;
 
@@ -1521,7 +1675,7 @@ class OpenGLDisplayDriver : DisplayDriver
       {
          oglDisplay.flipBufW = width;
          oglDisplay.flipBufH = height;
-#ifdef ES1_1
+#if defined(_GLES) || defined(_GLES2)
          result = true;
 #else
          oglDisplay.flippingBuffer = renew oglDisplay.flippingBuffer ColorAlpha [width * height];
@@ -1551,6 +1705,18 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void StartUpdate(Display display)
    {
+#if ENABLE_GL_VAO
+      if(glCaps_vao)
+      {
+         OGLDisplay oglDisplay = display.driverData;
+         glBindVertexArray(oglDisplay.vao);
+      }
+#endif
+      GLABBindBuffer(GL_ARRAY_BUFFER, 0);
+      GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+#if ENABLE_GL_SHADERS
+      activeProgram = 0;
+#endif
    }
 
    void EndUpdate(Display display)
@@ -1566,7 +1732,6 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__) || defined(USEPBUFFER)
       OGLDisplay oglDisplay = display.driverData;
 #endif
-      //Logf("DisplayScreen\n");
 
 #if !defined(__ANDROID__)
       /*glFlush();
@@ -1661,17 +1826,16 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__)
          //wglSwapLayerBuffers(oglDisplay.hdc,WGL_SWAP_MAIN_PLANE);
          SwapBuffers(oglDisplay.hdc);
+         //ecere::sys::Sleep(0.1);
 #elif defined(__unix__) || defined(__APPLE__)
       #if defined(__ANDROID__) || defined(__ODROID__)
-         eglSwapBuffers(eglDisplay, eglSurface);
+         egl_swap_buffers();
       #elif defined(__EMSCRIPTEN__)
-         glfwSwapBuffers();
       #else
          glXSwapBuffers(xGlobalDisplay, (GLXDrawable)display.window);
       #endif
 #endif
       }
-      //Logf("Out of DisplayScreen\n");
    }
 
    void FreeBitmap(DisplaySystem displaySystem, Bitmap bitmap)
@@ -1688,12 +1852,13 @@ class OpenGLDisplayDriver : DisplayDriver
    bool AllocateBitmap(DisplaySystem displaySystem, Bitmap bitmap, int width, int height, int stride, PixelFormat format, bool allocatePalette)
    {
       OGLSystem oglSystem = displaySystem.driverData;
+      GLCapabilities capabilities = oglSystem.capabilities;
       bool result = false;
       Bitmap mipMap { };
       GLuint glBitmap = 0;
 
       uint w = width, h = height;
-      if(oglSystem.pow2textures)
+      if(!capabilities.nonPow2Textures)
       {
          w = pow2i(w);
          h = pow2i(h);
@@ -1712,8 +1877,9 @@ class OpenGLDisplayDriver : DisplayDriver
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
-#if !defined(SHADERS)
-      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+#if ENABLE_GL_FFP
+      if(!capabilities.shaders)
+         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
 #endif
 
       mipMap.Allocate(null, w, h, w, pixelFormatRGBA, false);
@@ -1732,11 +1898,14 @@ class OpenGLDisplayDriver : DisplayDriver
       return result;
    }
 
-   bool MakeDDBitmap(DisplaySystem displaySystem, Bitmap bitmap, bool mipMaps)
+   bool MakeDDBitmap(DisplaySystem displaySystem, Bitmap bitmap, bool mipMaps, uint cubeMapFace)
    {
       bool result = false;
       OGLSystem oglSystem = displaySystem.driverData;
+      GLCapabilities capabilities = oglSystem.capabilities;
       Bitmap convBitmap = bitmap;
+      bool oldStyleCubeMap = (cubeMapFace >> 3) != 0;
+      int face = (cubeMapFace & 7) - 1;
       if(bitmap.keepData)
       {
          convBitmap = { };
@@ -1748,8 +1917,9 @@ class OpenGLDisplayDriver : DisplayDriver
       {
          int c, level;
          uint w = bitmap.width, h = bitmap.height;
-         GLuint glBitmap = 0;
-         if(oglSystem.pow2textures)
+         GLuint glBitmap = cubeMapFace && face > 0 ? (GLuint)(uintptr)bitmap.driverData : 0;
+         int target = cubeMapFace ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
+         if(!capabilities.nonPow2Textures)
          {
             w = pow2i(w);
             h = pow2i(h);
@@ -1766,7 +1936,8 @@ class OpenGLDisplayDriver : DisplayDriver
          // Switch ARGB to RGBA
          //if(bitmap.format != pixelFormatRGBA)
          {
-            for(c=0; c<bitmap.size; c++)
+            int size = convBitmap.stride * convBitmap.height;
+            for(c = 0; c < size; c++)
             {
                // ((ColorRGBA *)bitmap.picture)[c] = ((ColorAlpha *)bitmap.picture)[c];
                // TODO:
@@ -1776,32 +1947,76 @@ class OpenGLDisplayDriver : DisplayDriver
          }
          // convBitmap.pixelFormat = pixelFormat888;
 
+         if(cubeMapFace && oldStyleCubeMap)
+         {
+            if(face == 0 || face == 1 || face == 4 || face == 5)
+            {
+               uint w = convBitmap.width;
+               uint32 * tmp = new uint [convBitmap.width];
+               int x, y;
+               for(y = 0; y < convBitmap.height; y++)
+               {
+                  uint32 * pic = (uint32 *)((byte *)convBitmap.picture + y * w * 4);
+                  for(x = 0; x < w; x++)
+                     tmp[x] = pic[w-1-x];
+                  memcpy(pic, tmp, w*4);
+               }
+               delete tmp;
+            }
+            else if(face == 2 || face == 3)
+            {
+               int y;
+               Bitmap tmp { };
+               tmp.Allocate(null, convBitmap.width, convBitmap.height, 0, convBitmap.pixelFormat, false);
+               for(y = 0; y < convBitmap.height; y++)
+               {
+                  memcpy(tmp.picture + convBitmap.width * 4 * y,
+                     convBitmap.picture + (convBitmap.height-1-y) * convBitmap.width * 4,
+                     convBitmap.width * 4);
+               }
+               memcpy(convBitmap.picture, tmp.picture, convBitmap.sizeBytes);
+               delete tmp;
+            }
+         }
+
          glGetError();
-         glGenTextures(1, &glBitmap);
+         if(!glBitmap)
+            glGenTextures(1, &glBitmap);
          if(glBitmap == 0)
          {
             //int error = glGetError();
             return false;
          }
 
-         glBindTexture(GL_TEXTURE_2D, glBitmap);
+         glBindTexture(target, glBitmap);
          glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
 
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
+         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
-         //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+         //glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
-         //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
-         //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+         //glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP);
+         //glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+         glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+         glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+#ifndef GL_TEXTURE_WRAP_R
+   #define GL_TEXTURE_WRAP_R 0x8072
+#endif
+
+#if !defined(__EMSCRIPTEN__)
+         if(cubeMapFace)
+            glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+#endif
 
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 #ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
          glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0 );
 #endif
 
-#if !defined(SHADERS)
+#if ENABLE_GL_FFP
+      if(!capabilities.shaders)
          glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
 #endif
 
@@ -1818,6 +2033,7 @@ class OpenGLDisplayDriver : DisplayDriver
                if(mipMap.Allocate(null, w, h, w, convBitmap.pixelFormat, false))
                {
                   Surface mipSurface = mipMap.GetSurface(0,0,null);
+                  mipSurface.blend = false;
                   mipSurface.Filter(convBitmap, 0,0,0,0, w, h, convBitmap.width, convBitmap.height);
                   delete mipSurface;
                }
@@ -1836,7 +2052,7 @@ class OpenGLDisplayDriver : DisplayDriver
                //int width = 0;
                glGetError();
                // glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture);
-               glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture);
+               glTexImage2D(cubeMapFace ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + face : GL_TEXTURE_2D, level, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, mipMap.picture);
                //printf("Calling glTexImage2D\n");
                //glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width);
                //printf("width = %d (Should be %d, %d)\n", width, w, h);
@@ -1862,8 +2078,8 @@ class OpenGLDisplayDriver : DisplayDriver
             FreeBitmap(displaySystem, bitmap);
          else if(oglSystem.loadingFont)
          {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
             oglSystem.loadingFont = false;
          }
       }
@@ -1885,20 +2101,19 @@ class OpenGLDisplayDriver : DisplayDriver
    bool GetSurface(Display display, Surface surface, int x,int y, Box clip)
    {
       bool result = false;
+      OGLDisplay oglDisplay = display.driverData;
       OGLSurface oglSurface = surface.driverData = OGLSurface { };
-
-      //Logf("GetSurface\n");
-
       if(oglSurface)
       {
+         SETCAPS(oglDisplay.capabilities);
          if(displayWidth != display.width || displayHeight != display.height)
          {
             displayWidth = display.width;
             displayHeight = display.height;
 
             glViewport(0,0,display.width,display.height);
-            glLoadIdentity();
-            glOrtho(0,display.width,display.height,0,0.0,1.0);
+            GLLoadIdentity();
+            GLOrtho(0,display.width,display.height,0,0.0,1.0);
          }
 
          surface.offset.x = x;
@@ -1924,8 +2139,6 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       Box box;
 
-      //Logf("Clip\n");
-
       if(clip != null)
       {
          box = clip;
@@ -1962,9 +2175,11 @@ class OpenGLDisplayDriver : DisplayDriver
             uint row;
 
             glPixelStorei(GL_PACK_ALIGNMENT, 4);
+#if ENABLE_GL_LEGACY
             glPixelStorei(GL_PACK_ROW_LENGTH, bitmap.stride);
             glPixelStorei(GL_PACK_SKIP_ROWS, 0);
             glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
+#endif
             glReadPixels(x,display.height-h-y,w,h,GL_BGRA_EXT,GL_UNSIGNED_BYTE, flippingBuffer);
 
             // Need a flip...
@@ -1980,8 +2195,6 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       OGLSurface oglSurface = surface.driverData;
 
-      //Logf("SetForeground\n");
-
       oglSurface.foreground[0] = color.color.r/255.0f;
       oglSurface.foreground[1] = color.color.g/255.0f;
       oglSurface.foreground[2] = color.color.b/255.0f;
@@ -1995,8 +2208,6 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       OGLSurface oglSurface = surface.driverData;
 
-      //Logf("SetBackground\n");
-
       oglSurface.background[0] = color.color.r/255.0f;
       oglSurface.background[1] = color.color.g/255.0f;
       oglSurface.background[2] = color.color.b/255.0f;
@@ -2022,15 +2233,11 @@ class OpenGLDisplayDriver : DisplayDriver
    void PutPixel(Display display, Surface surface,int x,int y)
    {
       OGLSurface oglSurface = surface.driverData;
-
-      //Logf("PutPixel\n");
-
-      glColor4fv(oglSurface.foreground);
-      glBegin(GL_POINTS);
+      GLColor4fv(oglSurface.foreground);
+      GLBegin(GL_POINTS);
       // glVertex2i(x+surface.offset.x, y+surface.offset.y);
-      glVertex2f(x+surface.offset.x + 0.5f, y+surface.offset.y + 0.5f);
-
-      glEnd();
+      GLVertex2f(x+surface.offset.x + 0.5f, y+surface.offset.y + 0.5f);
+      GLEnd();
    }
 
    void DrawLine(Display display, Surface surface, int _x1, int _y1, int _x2, int _y2)
@@ -2056,30 +2263,26 @@ class OpenGLDisplayDriver : DisplayDriver
       x2 += surface.offset.x;
       y2 += surface.offset.y;
 
-      //Logf("Line\n");
-
-      glColor4fv(oglSurface.foreground);
-      glBegin(GL_LINES);
-#if defined(ES1_1) || defined(EM_MODE) || defined(SHADERS)
+      GLColor4fv(oglSurface.foreground);
+      GLBegin(GL_LINES);
       if(stippleEnabled)
       {
-         glTexCoord2f(0.5f, 0);
-         glVertex2f(x1 + 0.5f, y1 + 0.5f);
-         glTexCoord2f(Max(x2-x1, y2-y1) + 0.5f, 0);
-         glVertex2f(x2 + 0.5f, y2 + 0.5f);
+         GLTexCoord2f(0.5f, 0);
+         GLVertex2f(x1 + 0.5f, y1 + 0.5f);
+         GLTexCoord2f(Max(x2-x1, y2-y1) + 0.5f, 0);
+         GLVertex2f(x2 + 0.5f, y2 + 0.5f);
       }
       else
-#endif
       {
          /*
-         glVertex2i(x1, y1);
-         glVertex2i(x2, y2);
+         GLVertex2i(x1, y1);
+         GLVertex2i(x2, y2);
          */
-         glVertex2f(x1 + 0.5f, y1 + 0.5f);
-         glVertex2f(x2 + 0.5f, y2 + 0.5f);
+         GLVertex2f(x1 + 0.5f, y1 + 0.5f);
+         GLVertex2f(x2 + 0.5f, y2 + 0.5f);
       }
 
-      glEnd();
+      GLEnd();
    }
 
    void Rectangle(Display display, Surface surface,int x1,int y1,int x2,int y2)
@@ -2090,72 +2293,58 @@ class OpenGLDisplayDriver : DisplayDriver
       x2 += surface.offset.x;
       y2 += surface.offset.y;
 
-      //Logf("Rectangle\n");
-
-      glColor4fv(oglSurface.foreground);
-#if defined(ES1_1) || defined(EM_MODE) || defined(SHADERS)
+      GLColor4fv(oglSurface.foreground);
       if(stippleEnabled)
       {
-         glBegin(GL_LINES);
-
-         glTexCoord2f(0.5f, 0);
-         glVertex2f(x1 + 0.5f, y1 + 0.5f);
-         glTexCoord2f(y2-y1 + 0.5f, 0);
-         glVertex2f(x1 + 0.5f, y2 + 0.5f);
-
-         glTexCoord2f(0.5f, 0);
-         glVertex2f(x1 + 0.5f, y2 + 0.5f);
-         glTexCoord2f(x2 - x1 + 0.5f, 0);
-         glVertex2f(x2 + 0.5f, y2 + 0.5f);
-
-         glTexCoord2f(0.5f, 0);
-         glVertex2f(x2 + 0.5f, y2 + 0.5f);
-         glTexCoord2f(y1 - y2 + 0.5f, 0);
-         glVertex2f(x2 + 0.5f, y1 + 0.5f);
-
-         glTexCoord2f(0.5f, 0);
-         glVertex2f(x2 + 0.5f, y1 + 0.5f);
-         glTexCoord2f(x1 - x2 + 0.5f, 0);
-         glVertex2f(x1 + 0.5f, y1 + 0.5f);
+         GLBegin(GL_LINES);
+
+         GLTexCoord2f(0.5f, 0);
+         GLVertex2f(x1 + 0.5f, y1 + 0.5f);
+         GLTexCoord2f(y2-y1 + 0.5f, 0);
+         GLVertex2f(x1 + 0.5f, y2 + 0.5f);
+
+         GLTexCoord2f(0.5f, 0);
+         GLVertex2f(x1 + 0.5f, y2 + 0.5f);
+         GLTexCoord2f(x2 - x1 + 0.5f, 0);
+         GLVertex2f(x2 + 0.5f, y2 + 0.5f);
+
+         GLTexCoord2f(0.5f, 0);
+         GLVertex2f(x2 + 0.5f, y2 + 0.5f);
+         GLTexCoord2f(y1 - y2 + 0.5f, 0);
+         GLVertex2f(x2 + 0.5f, y1 + 0.5f);
+
+         GLTexCoord2f(0.5f, 0);
+         GLVertex2f(x2 + 0.5f, y1 + 0.5f);
+         GLTexCoord2f(x1 - x2 + 0.5f, 0);
+         GLVertex2f(x1 + 0.5f, y1 + 0.5f);
       }
       else
-#endif
       {
-         glBegin(GL_LINE_LOOP);
+         GLBegin(GL_LINE_LOOP);
          /*
          glVertex2i(x1, y1);
          glVertex2i(x1, y2);
          glVertex2i(x2, y2);
          glVertex2i(x2, y1);
          */
-         glVertex2f(x1 + 0.5f, y1 + 0.5f);
-         glVertex2f(x1 + 0.5f, y2 + 0.5f);
-         glVertex2f(x2 + 0.5f, y2 + 0.5f);
-         glVertex2f(x2 + 0.5f, y1 + 0.5f);
+         GLVertex2f(x1 + 0.5f, y1 + 0.5f);
+         GLVertex2f(x1 + 0.5f, y2 + 0.5f);
+         GLVertex2f(x2 + 0.5f, y2 + 0.5f);
+         GLVertex2f(x2 + 0.5f, y1 + 0.5f);
       }
-      glEnd();
+      GLEnd();
    }
 
    void Area(Display display, Surface surface,int x1,int y1,int x2,int y2)
    {
       OGLSurface oglSurface = surface.driverData;
-      //Logf("Area\n");
 
-      glColor4fv(oglSurface.background);
+      GLColor4fv(oglSurface.background);
 
-#ifdef EM_MODE
-      glBegin(GL_QUADS);
-      glVertex2f(x1+surface.offset.x, y1+surface.offset.y);
-      glVertex2f(x1+surface.offset.x, y2+surface.offset.y+1);
-      glVertex2f(x2+surface.offset.x+1, y2+surface.offset.y+1);
-      glVertex2f(x2+surface.offset.x+1, y1+surface.offset.y);
-      glEnd();
-#else
-      glRecti(x1+surface.offset.x, y1+surface.offset.y,
+      GLRecti(x1+surface.offset.x, y1+surface.offset.y,
               x2+surface.offset.x + 1, y2+surface.offset.y + 1);
-#endif
       /*
-      glRectf(x1+surface.offset.x, y1+surface.offset.y,
+      GLRectf(x1+surface.offset.x, y1+surface.offset.y,
               x2+surface.offset.x + 1, y2+surface.offset.y + 1);
       */
    }
@@ -2188,62 +2377,65 @@ class OpenGLDisplayDriver : DisplayDriver
    void Blit(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h)
    {
       OGLSurface oglSurface = surface.driverData;
+      GLuint tex = (GLuint)(uintptr)bitmap.driverData;
+      if(!tex) return;
 
       if(!oglSurface.writingText)
       {
          // glTranslatef(-0.375f, -0.375f, 0.0f);
          GLSetupTexturing(true);
-         glColor4fv(oglSurface.bitmapMult);
+         GLColor4fv(oglSurface.bitmapMult);
+         glBindTexture(GL_TEXTURE_2D, tex);
+         GLBegin(GLIMTKMode::quads);
+      }
+      else if(lastBlitTex != tex)
+      {
+         if(lastBlitTex)
+            GLEnd();
+         glBindTexture(GL_TEXTURE_2D, tex);
+         GLBegin(GLIMTKMode::quads);
+         lastBlitTex = tex;
       }
-      else if(oglSurface.xOffset)
-         glTranslated(oglSurface.xOffset / 64.0/*-0.375*/, 0.0, 0.0);
-
-      glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)bitmap.driverData);
-      glBegin(GLIMTKMode::quads);
 
       if(h < 0)
       {
-         glTexCoord2f((float)sx/ bitmap.width, (float)(sy-h)/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy+surface.offset.y);
-         glTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy-h)/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
-         glTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y);
-         glTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy-h+surface.offset.y);
+         GLTexCoord2f((float)sx/ bitmap.width, (float)(sy-h)/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy-h)/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y);
+         GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy-h+surface.offset.y);
       }
       else
       {
          /*
-         glTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy+surface.offset.y);
-         glTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
-         glTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy+h)/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy+h+surface.offset.y);
-         glTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy+h+surface.offset.y);
+         GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy+h)/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy+h+surface.offset.y);
+         GLTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy+h+surface.offset.y);
          */
 
-         glTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height);
-         glVertex2f((float)dx+surface.offset.x, (float)dy+surface.offset.y);
-         glTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height);
-         glVertex2f((float)dx+w+surface.offset.x, (float)dy+surface.offset.y);
-         glTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy+h)/ bitmap.height);
-         glVertex2f((float)dx+w+surface.offset.x, (float)dy+h+surface.offset.y);
-         glTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height);
-         glVertex2f((float)dx+surface.offset.x, (float)dy+h+surface.offset.y);
+         GLTexCoord2f((float)sx / bitmap.width, (float)sy/ bitmap.height);
+         GLVertex2f((float)dx+surface.offset.x, (float)dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+w)/ bitmap.width, (float)sy/ bitmap.height);
+         GLVertex2f((float)dx+w+surface.offset.x, (float)dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+w) / bitmap.width, (float)(sy+h)/ bitmap.height);
+         GLVertex2f((float)dx+w+surface.offset.x, (float)dy+h+surface.offset.y);
+         GLTexCoord2f((float)sx/ bitmap.width, (float)(sy+h)/ bitmap.height);
+         GLVertex2f((float)dx+surface.offset.x, (float)dy+h+surface.offset.y);
       }
-      glEnd();
-
       if(!oglSurface.writingText)
       {
+         GLEnd();
          GLSetupTexturing(false);
-
          //glTranslate(0.375, 0.375, 0.0);
       }
-      else if(oglSurface.xOffset)
-         glTranslated(-oglSurface.xOffset / 64.0/*+0.375*/, 0.0, 0.0);
    }
 
    void Stretch(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
@@ -2255,40 +2447,40 @@ class OpenGLDisplayDriver : DisplayDriver
       GLSetupTexturing(true);
       glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)bitmap.driverData);
 
-      glColor4fv(oglSurface.bitmapMult);
+      GLColor4fv(oglSurface.bitmapMult);
 
-      glBegin(GLIMTKMode::quads);
+      GLBegin(GLIMTKMode::quads);
 
       if(h < 0)
       {
-         glTexCoord2f((float)(sx)/ bitmap.width, (float)(sy+sh)/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx)/ bitmap.width, (float)(sy+sh)/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy+surface.offset.y);
 
-         glTexCoord2f((float)(sx+sw) / bitmap.width, (float)(sy+sh)/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+sw) / bitmap.width, (float)(sy+sh)/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
 
-         glTexCoord2f((float)(sx+sw)/ bitmap.width, (float)(sy)/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y);
+         GLTexCoord2f((float)(sx+sw)/ bitmap.width, (float)(sy)/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy-h+surface.offset.y);
 
-         glTexCoord2f((float)(sx) / bitmap.width, (float)(sy)/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy-h+surface.offset.y);
+         GLTexCoord2f((float)(sx) / bitmap.width, (float)(sy)/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy-h+surface.offset.y);
       }
       else
       {
-         glTexCoord2f((float)(sx) / bitmap.width, (float)(sy)/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx) / bitmap.width, (float)(sy)/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy+surface.offset.y);
 
-         glTexCoord2f((float)(sx+sw)/ bitmap.width, (float)(sy)/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
+         GLTexCoord2f((float)(sx+sw)/ bitmap.width, (float)(sy)/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy+surface.offset.y);
 
-         glTexCoord2f((float)(sx+sw) / bitmap.width, (float)(sy+sh)/ bitmap.height);
-         glVertex2i(dx+w+surface.offset.x, dy+h+surface.offset.y);
+         GLTexCoord2f((float)(sx+sw) / bitmap.width, (float)(sy+sh)/ bitmap.height);
+         GLVertex2i(dx+w+surface.offset.x, dy+h+surface.offset.y);
 
-         glTexCoord2f((float)(sx)/ bitmap.width, (float)(sy+sh)/ bitmap.height);
-         glVertex2i(dx+surface.offset.x, dy+h+surface.offset.y);
+         GLTexCoord2f((float)(sx)/ bitmap.width, (float)(sy+sh)/ bitmap.height);
+         GLVertex2i(dx+surface.offset.x, dy+h+surface.offset.y);
       }
 
-      glEnd();
+      GLEnd();
 
       GLSetupTexturing(false);
 
@@ -2302,12 +2494,9 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void StretchDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
    {
-#if !defined(EM_MODE)
       float s2dw,s2dh,d2sw,d2sh;
       //bool flipX = false, flipY = false;
 
-      //Logf("StretchDI\n");
-
       if(Sgn(w) != Sgn(sw))
       {
          w = Abs(w);
@@ -2387,28 +2576,27 @@ class OpenGLDisplayDriver : DisplayDriver
       if(bitmap.pixelFormat == pixelFormat888 && !bitmap.paletteShades)
       {
          glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-         glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap.stride);
-         glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
-         glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
-#if !defined(SHADERS)
-         glRasterPos2d(dx,dy);
-         //glPixelZoom(flipX ? -s2dw : s2dw, flipY ? s2dh : -s2dh);
-         glPixelZoom(s2dw, -s2dh);
-         glDrawPixels(sw,sh,GL_BGRA_EXT,GL_UNSIGNED_BYTE, bitmap.picture);
+#if ENABLE_GL_LEGACY
+         if(glCaps_legacy)
+         {
+            glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap.stride);
+            glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
+            glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
+            glRasterPos2d(dx,dy);
+            //glPixelZoom(flipX ? -s2dw : s2dw, flipY ? s2dh : -s2dh);
+            glPixelZoom(s2dw, -s2dh);
+            glDrawPixels(sw,sh,GL_BGRA_EXT,GL_UNSIGNED_BYTE, bitmap.picture);
+            glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+            glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+            glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+         }
 #endif
          glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-         glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
-         glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
       }
-#endif
    }
 
    void BlitDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h)
    {
-#if !defined(EM_MODE)
-      //Logf("BlitDI\n");
-
       //Clip against the edges of the source
       if(sx<0)
       {
@@ -2456,20 +2644,22 @@ class OpenGLDisplayDriver : DisplayDriver
       if(bitmap.pixelFormat == pixelFormat888 && !bitmap.paletteShades)
       {
          glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
-         glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap.stride);
-         glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
-         glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
-#if !defined(SHADERS)
-         glRasterPos2d(dx,dy);
-         glPixelZoom(1,-1);
-         glDrawPixels(w,h,GL_BGRA_EXT,GL_UNSIGNED_BYTE, bitmap.picture);
+#if ENABLE_GL_LEGACY
+         if(glCaps_legacy)
+         {
+            glPixelStorei(GL_UNPACK_ROW_LENGTH, bitmap.stride);
+            glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
+            glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
+            glRasterPos2d(dx,dy);
+            glPixelZoom(1,-1);
+            glDrawPixels(w,h,GL_BGRA_EXT,GL_UNSIGNED_BYTE, bitmap.picture);
+            glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+            glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
+            glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
+         }
 #endif
          glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-         glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
-         glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
       }
-#endif
    }
 
    void FilterDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
@@ -2498,43 +2688,54 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void WriteText(Display display, Surface surface, int x, int y, const char * text, int len, int prevGlyph, int * rPrevGlyph)
    {
-      OGLSurface oglSurface = surface.driverData;
-      OGLSystem oglSystem = display.displaySystem.driverData;
-      oglSystem.loadingFont = true;
+      if(len && text[0] && surface.font)
+      {
+         OGLSurface oglSurface = surface.driverData;
+         OGLSystem oglSystem = display.displaySystem.driverData;
+         oglSystem.loadingFont = true;
+
+         //glTranslated(-0.375, -0.375, 0.0);
 
-      //glTranslated(-0.375, -0.375, 0.0);
+         if(surface.textOpacity)
+         {
+            int w = 0, h, adv = 0;
+            FontExtent(display.displaySystem, surface.font, text, len, &w, &h, 0, null, &adv);
+            w += adv;
+            display.displaySystem.driver.Area(display, surface,x,y,x+w-1,y+h-1);
+         }
 
-      //Logf("Blit\n");
+         oglSurface.writingText = true;
 
-      if(surface.textOpacity)
-      {
-         int w, h, adv;
-         FontExtent(display.displaySystem, surface.font, text, len, &w, &h, 0, null, &adv);
-         w += adv;
-         display.displaySystem.driver.Area(display, surface,x,y,x+w-1,y+h-1);
-      }
+         GLSetupTexturing(true);
 
-      oglSurface.writingText = true;
+         x <<= 6;
+         if(surface.font.outlineSize)
+         {
+            ColorAlpha outlineColor = surface.outlineColor;
+            int fx = x;
+
+            GLColor4ub(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, outlineColor.a);
+            oglSurface.writingOutline = true;
+            lastBlitTex = 0;
+            oglSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &fx, y, prevGlyph, rPrevGlyph, null);
+            if(lastBlitTex) GLEnd();
+            oglSurface.writingOutline = false;
+         }
+         GLColor4fv(oglSurface.foreground);
 
-      GLSetupTexturing(true);
+         lastBlitTex = 0;
+         oglSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &x, y, prevGlyph, rPrevGlyph, null);
 
-      if(surface.font.outlineSize)
-      {
-         ColorAlpha outlineColor = surface.outlineColor;
-         glColor4ub(outlineColor.color.r, outlineColor.color.g, outlineColor.color.b, outlineColor.a);
-         oglSurface.writingOutline = true;
-         ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
-         oglSurface.writingOutline = false;
-      }
-      glColor4fv(oglSurface.foreground);
+         if(lastBlitTex) GLEnd();
+         lastBlitTex = 0;
 
-      ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len, prevGlyph, rPrevGlyph);
-      oglSurface.writingText = false;
-      oglSystem.loadingFont = false;
+         oglSurface.writingText = false;
+         oglSystem.loadingFont = false;
 
-      GLSetupTexturing(false);
+         GLSetupTexturing(false);
 
-      //glTranslated(0.375, 0.375, 0.0);
+         //glTranslated(0.375, 0.375, 0.0);
+      }
    }
 
    void TextFont(Display display, Surface surface, Font font)
@@ -2564,48 +2765,74 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void LineStipple(Display display, Surface surface, uint32 stipple)
    {
-      //Logf("Stipple\n");
-
       if(stipple)
       {
-#if defined(ES1_1) || defined(EM_MODE) || defined(SHADERS)
-         stippleEnabled = true;
-         glesLineStipple(1, (uint16)stipple);
-#else
-         glLineStipple(1, (uint16)stipple);
-         glEnable(GL_LINE_STIPPLE);
+#if ENABLE_GL_LEGACY
+         if(glCaps_legacy)
+         {
+            glLineStipple(1, (uint16)stipple);
+            glEnable(GL_LINE_STIPPLE);
+         }
+         else
 #endif
+         {
+            stippleEnabled = true;
+            glsupLineStipple(1, (uint16)stipple);
+         }
       }
       else
       {
-#if defined(ES1_1) || defined(EM_MODE) || defined(SHADERS)
-         stippleEnabled = false;
-         glMatrixMode(GL_TEXTURE);
-         glLoadIdentity();
-         glMatrixMode(MatrixMode::projection);
-         GLSetupTexturing(false);   // TODO: Special shading code for stipple?
-#else
-         glDisable(GL_LINE_STIPPLE);
+#if ENABLE_GL_LEGACY
+         if(glCaps_legacy)
+            glDisable(GL_LINE_STIPPLE);
+         else
 #endif
+         {
+            stippleEnabled = false;
+            GLMatrixMode(GL_TEXTURE);
+            GLLoadIdentity();
+            GLMatrixMode(MatrixMode::projection);
+            GLSetupTexturing(false);   // TODO: Special shading code for stipple?
+         }
       }
    }
+#if ENABLE_GL_FFP
+   void ::disableRemainingTMUs(Display display, int lastTMU)
+   {
+      OGLDisplay oglDisplay = display.driverData;
+      int t;
+      for(t = lastTMU; t < oglDisplay.maxTMU; t++)
+      {
+         glActiveTexture(GL_TEXTURE0 + t);
+         glClientActiveTexture(GL_TEXTURE0 + t);
+         glDisable(GL_TEXTURE_2D);
+         glDisable(GL_TEXTURE_CUBE_MAP);
+         GLDisableClientState(TEXCOORDS);
+      }
+      glActiveTexture(GL_TEXTURE0);
+      glClientActiveTexture(GL_TEXTURE0);
+      oglDisplay.maxTMU = lastTMU;
+   }
+#endif
+
 #if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
    void SetRenderState(Display display, RenderState state, uint value)
    {
       OGLDisplay oglDisplay = display.driverData;
-      //Logf("RenderState\n");
-
       switch(state)
       {
          case antiAlias:
+#ifndef __EMSCRIPTEN__
             if(value)
-               glEnable(GL_MULTISAMPLE_ARB);
+               glEnable(GL_MULTISAMPLE);
             else
-               glDisable(GL_MULTISAMPLE_ARB);
+               glDisable(GL_MULTISAMPLE);
+#endif
             break;
          case fillMode:
-#if !defined(ES1_1)
-            glPolygonMode(GL_FRONT_AND_BACK, ((FillModeValue)value == solid) ? GL_FILL : GL_LINE);
+#if ENABLE_GL_LEGACY
+            if(glCaps_legacy)
+               glPolygonMode(GL_FRONT_AND_BACK, ((FillModeValue)value == solid) ? GL_FILL : GL_LINE);
 #endif
             break;
          case depthTest:
@@ -2617,25 +2844,47 @@ class OpenGLDisplayDriver : DisplayDriver
             break;
          case fogColor:
          {
-#if !defined(SHADERS)
             float color[4] = { ((Color)value).r/255.0f, ((Color)value).g/255.0f, ((Color)value).b/255.0f, 1.0f };
-            glFogfv(GL_FOG_COLOR, (float *)&color);
+#if ENABLE_GL_SHADERS
+            if(glCaps_shaders)
+               defaultShader.setFogColor(color[0], color[1], color[2]);
+#endif
+
+#if ENABLE_GL_FFP
+            if(!glCaps_shaders)
+               glFogfv(GL_FOG_COLOR, (float *)&color);
 #endif
             break;
          }
          case fogDensity:
-#if !defined(SHADERS)
-            glFogf(GL_FOG_DENSITY, (float)(RenderStateFloat { ui = value }.f * nearPlane));
+#if ENABLE_GL_SHADERS
+            if(glCaps_shaders)
+               defaultShader.setFogDensity((float)(RenderStateFloat { ui = value }.f * nearPlane));
+#endif
+
+#if ENABLE_GL_FFP
+            if(!glCaps_shaders)
+               glFogf(GL_FOG_DENSITY, (float)(RenderStateFloat { ui = value }.f * nearPlane));
 #endif
             break;
          case blend:
+//#if !defined(__EMSCRIPTEN__)
             if(value) glEnable(GL_BLEND); else glDisable(GL_BLEND);
+//#endif
             break;
          case ambient:
          {
-#if !defined(EM_MODE) && !defined(SHADERS)
-            float ambient[4] = { ((Color)value).r/255.0f, ((Color)value).g/255.0f, ((Color)value).b/255.0f, 1.0f };
-            glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+#if ENABLE_GL_SHADERS
+            if(glCaps_shaders)
+               defaultShader.setGlobalAmbient(((Color)value).r / 255.0f, ((Color)value).g / 255.0f, ((Color)value).b / 255.0f, 1.0f);
+#endif
+
+#if ENABLE_GL_FFP
+            if(!glCaps_shaders)
+            {
+               float ambient[4] = { ((Color)value).r/255.0f, ((Color)value).g/255.0f, ((Color)value).b/255.0f, 1.0f };
+               glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+            }
 #endif
             break;
          }
@@ -2647,7 +2896,8 @@ class OpenGLDisplayDriver : DisplayDriver
          case vSync:
          {
 #if defined(__WIN32__)
-            wglSwapIntervalEXT(value ? 1 : 0);
+            if(wglSwapIntervalEXT)
+               wglSwapIntervalEXT(value ? 1 : 0);
 #endif
             break;
          }
@@ -2656,165 +2906,167 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void SetLight(Display display, int id, Light light)
    {
-#if !defined(EM_MODE) && !defined(SHADERS)
-      //Logf("SetLight\n");
+#if ENABLE_GL_SHADERS
+      if(glCaps_shaders)
+         defaultShader.setLight(display, id, light);
+#endif
 
-      if(light != null)
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
       {
-         Object lightObject = light.lightObject;
-         float position[4] = { 0, 0, 0, 0 };
-         float color[4] = { 0, 0, 0, 1 };
+         if(light != null && !light.flags.off)
+         {
+            Object lightObject = light.lightObject;
+            float position[4] = { 0, 0, 0, 0 };
+            float color[4] = { 0, 0, 0, 1 };
+            Vector3D l;
 
-         glEnable(GL_LIGHT0 + id);
-         /*
-         glLightfv(GL_LIGHT0 + id, GL_DIFFUSE, (float *)&light.diffuse);
-         glLightfv(GL_LIGHT0 + id, GL_AMBIENT, (float *)&light.ambient);
-         glLightfv(GL_LIGHT0 + id, GL_SPECULAR,(float *)&light.specular);
-         */
+            glEnable(GL_LIGHT0 + id);
 
-         if(!light.multiplier) light.multiplier = 1.0f;
+            if(!light.multiplier) light.multiplier = 1.0f;
 
-         color[0] = light.diffuse.r * light.multiplier;
-         color[1] = light.diffuse.g * light.multiplier;
-         color[2] = light.diffuse.b * light.multiplier;
-         glLightfv(GL_LIGHT0 + id, GL_DIFFUSE, color);
+            GLFlushMatrices();
 
-         color[0] = light.ambient.r * light.multiplier;
-         color[1] = light.ambient.g * light.multiplier;
-         color[2] = light.ambient.b * light.multiplier;
-         glLightfv(GL_LIGHT0 + id, GL_AMBIENT, color);
-         color[0] = light.specular.r * light.multiplier;
-         color[1] = light.specular.g * light.multiplier;
-         color[2] = light.specular.b * light.multiplier;
-         glLightfv(GL_LIGHT0 + id, GL_SPECULAR,color);
+            color[0] = light.diffuse.r * light.multiplier;
+            color[1] = light.diffuse.g * light.multiplier;
+            color[2] = light.diffuse.b * light.multiplier;
+            glLightfv(GL_LIGHT0 + id, GL_DIFFUSE, color);
 
-         if(lightObject)
-         {
-            Vector3D positionVector;
-            if(light.flags.spot)
+            color[0] = light.ambient.r * light.multiplier;
+            color[1] = light.ambient.g * light.multiplier;
+            color[2] = light.ambient.b * light.multiplier;
+            glLightfv(GL_LIGHT0 + id, GL_AMBIENT, color);
+
+            color[0] = light.specular.r * light.multiplier;
+            color[1] = light.specular.g * light.multiplier;
+            color[2] = light.specular.b * light.multiplier;
+            glLightfv(GL_LIGHT0 + id, GL_SPECULAR,color);
+
+            if(lightObject)
             {
-               if(lightObject.flags.root || !lightObject.parent)
+               // Positional Lights, including Spot Lights (and omni light with flags.spot not set)
+               Matrix * mat = &lightObject.matrix;
+               l = { mat->m[3][0], mat->m[3][1], mat->m[3][2] };
+               if(display.display3D && display.display3D.camera)
+                  l.Subtract(l, display.display3D.camera.cPosition);
+
+               position[0] = (float)l.x, position[1] = (float)l.y, position[2] = (float)l.z, position[3] = 1;
+
+               if(light.flags.attenuation)
                {
-                  positionVector = lightObject.transform.position;
-                  positionVector.Subtract(positionVector, display.display3D.camera.cPosition);
+                  glLightf(GL_LIGHT0 + id, GL_CONSTANT_ATTENUATION, light.Kc);
+                  glLightf(GL_LIGHT0 + id, GL_LINEAR_ATTENUATION, light.Kl);
+                  glLightf(GL_LIGHT0 + id, GL_QUADRATIC_ATTENUATION, light.Kq);
                }
                else
                {
-                  positionVector.MultMatrix(lightObject.transform.position, lightObject.parent.matrix);
-                  if(display.display3D.camera)
-                     positionVector.Subtract(positionVector, display.display3D.camera.cPosition);
+                  glLightf(GL_LIGHT0 + id, GL_CONSTANT_ATTENUATION, 1);
+                  glLightf(GL_LIGHT0 + id, GL_LINEAR_ATTENUATION, 0);
+                  glLightf(GL_LIGHT0 + id, GL_QUADRATIC_ATTENUATION, 0);
                }
-               position[3] = 1;
-            }
-            else
-            {
-               if(!light.direction.x && !light.direction.y && !light.direction.z)
+
+               if((light.flags.spot && light.fallOff < 360) || (lightObject && (light.direction.x || light.direction.y || light.direction.z)))
                {
-                  Vector3Df vector { 0,0,-1 };
-                  Matrix mat;
-                  mat.RotationQuaternion(light.orientation);
-                  positionVector.MultMatrixf(vector, mat);
+                  // Figure out exponent out of the hot spot
+                  #define MAXLIGHT  0.9
+                  float exponent = light.flags.spot ? (float)(log(MAXLIGHT) / log(cos(light.hotSpot / 2))) : 1;
+                  Degrees cutOff = light.flags.spot ? light.fallOff/2 : 90;
+                  float direction[4] = { (float)light.direction.x, (float)light.direction.y, (float)light.direction.z, 1 };
+
+                  glLightfv(GL_LIGHT0 + id, GL_SPOT_DIRECTION, direction);
+                  glLightf(GL_LIGHT0 + id, GL_SPOT_CUTOFF, (float)cutOff);
+                  glLightf(GL_LIGHT0 + id, GL_SPOT_EXPONENT, exponent);
                }
                else
                {
-                  positionVector = light.direction;
-                  position[3] = 1;
+                  float d[4] = { 0, 0, 1, 0 };
+                  glLightfv(GL_LIGHT0 + id, GL_SPOT_DIRECTION, d);
+                  glLightf(GL_LIGHT0 + id, GL_SPOT_CUTOFF, 180);
+                  glLightf(GL_LIGHT0 + id, GL_SPOT_EXPONENT, 1);
                }
-            }
 
-            position[0] = (float)positionVector.x;
-            position[1] = (float)positionVector.y;
-            position[2] = (float)positionVector.z;
-
-            glLightfv(GL_LIGHT0 + id, GL_POSITION, position);
+               /*
+               if(lightObject)
+               {
+                  // Display Light Position
+                  glDisable(GL_LIGHTING);
+                  glDisable(GL_DEPTH_TEST);
+                  glColor3f(1,1,1);
+                  glPointSize(10);
+                  glBegin(GL_POINTS);
+                  glVertex3fv(position);
+                  glEnd();
+                  glEnable(GL_DEPTH_TEST);
+                  glEnable(GL_LIGHTING);
+
+
+                  // Display Target
+                  if(lightObject.flags.root || !lightObject.parent)
+                  {
+                     positionVector = light.target.transform.position;
+                     positionVector.Subtract(positionVector, display.camera.cPosition);
+                  }
+                  else
+                  {
+                     positionVector.MultMatrix(light.target.transform.position,
+                        lightObject.light.target.parent.matrix);
+                     positionVector.Subtract(positionVector, display.camera.cPosition);
+                  }
 
-            /*
-            // Display Light Position
-            glDisable(GL_LIGHTING);
-            glDisable(GL_DEPTH_TEST);
-            glColor3f(1,1,1);
-            glPointSize(10);
-            glBegin(GL_POINTS);
-            glVertex3fv(position);
-            glEnd();
-            glEnable(GL_DEPTH_TEST);
-            glEnable(GL_LIGHTING);
-
-
-            // Display Target
-            if(lightObject.flags.root || !lightObject.parent)
-            {
-               positionVector = light.target.transform.position;
-               positionVector.Subtract(positionVector, display.camera.cPosition);
+                  position[0] = positionVector.x;
+                  position[1] = positionVector.y;
+                  position[2] = positionVector.z;
+
+                  glDisable(GL_LIGHTING);
+                  glDisable(GL_DEPTH_TEST);
+                  glColor3f(1,1,0);
+                  glPointSize(10);
+                  glBegin(GL_POINTS);
+                  glVertex3fv(position);
+                  glEnd();
+                  glEnable(GL_DEPTH_TEST);
+                  glEnable(GL_LIGHTING);
+               }
+               */
             }
             else
             {
-               positionVector.MultMatrix(light.target.transform.position,
-                  lightObject.light.target.parent.matrix);
-               positionVector.Subtract(positionVector, display.camera.cPosition);
-            }
-
-            position[0] = positionVector.x;
-            position[1] = positionVector.y;
-            position[2] = positionVector.z;
-
-            glDisable(GL_LIGHTING);
-            glDisable(GL_DEPTH_TEST);
-            glColor3f(1,1,0);
-            glPointSize(10);
-            glBegin(GL_POINTS);
-            glVertex3fv(position);
-            glEnd();
-            glEnable(GL_DEPTH_TEST);
-            glEnable(GL_LIGHTING);
-            */
-
-            if(light.flags.attenuation)
-            {
-               glLightf(GL_LIGHT0 + id, GL_CONSTANT_ATTENUATION, light.Kc);
-               glLightf(GL_LIGHT0 + id, GL_LINEAR_ATTENUATION, light.Kl);
-               glLightf(GL_LIGHT0 + id, GL_QUADRATIC_ATTENUATION, light.Kq);
+               // Directional Light
+               Vector3D vector { 0,0,-1 };
+               Vector3D direction;
+               Matrix mat;
+               mat.RotationQuaternion(light.orientation);
+               direction.MultMatrix(vector, mat);
+               l.Normalize(direction);
+               position[0] = (float)l.x, position[1] = (float)l.y, position[2] = (float)l.z, position[3] = 0;
             }
-
-            if(light.flags.spot)
+            glLightfv(GL_LIGHT0 + id, GL_POSITION, position);
+            if(display.display3D)
             {
-               float exponent = 0;
-               #define MAXLIGHT  0.9
-               float direction[4] = { (float)light.direction.x, (float)light.direction.y, (float)light.direction.z, 1 };
-               // Figure out exponent out of the hot spot
-               exponent = (float)(log(MAXLIGHT) / log(cos((light.hotSpot / 2))));
-
-               glLightfv(GL_LIGHT0 + id, GL_SPOT_DIRECTION, direction);
-               glLightf(GL_LIGHT0 + id, GL_SPOT_CUTOFF, (float)(light.fallOff / 2));
-               glLightf(GL_LIGHT0 + id, GL_SPOT_EXPONENT, exponent);
+               Matrix m;
+               Vector3Df v { position[0], position[1], position[2] };
+               Vector3Df l;
+               float * lp = display.display3D.light0Pos;
+               if(display.display3D.camera)
+                  m = display.display3D.camera.viewMatrix;
+               else
+                  m.Identity();
+               l.MultMatrix(v, m);
+               lp[0] = l.x;
+               lp[1] =-l.y;
+               lp[2] =-l.z;
+               lp[3] = position[3];
             }
          }
          else
-         {
-
-            Vector3Df vector { 0,0,-1 };
-            Vector3Df direction;
-            Matrix mat;
-
-            mat.RotationQuaternion(light.orientation);
-            direction.MultMatrix(vector, mat);
-
-            position[0] = direction.x;
-            position[1] = direction.y;
-            position[2] = direction.z;
-
-            glLightfv(GL_LIGHT0 + id, GL_POSITION, position);
-         }
+            glDisable(GL_LIGHT0 + id);
       }
-      else
-         glDisable(GL_LIGHT0 + id);
 #endif
    }
 
    void SetCamera(Display display, Surface surface, Camera camera)
    {
       OGLDisplay oglDisplay = display.driverData;
-      //Logf("SetCamera\n");
 
       if(surface && camera)
       {
@@ -2832,10 +3084,15 @@ class OpenGLDisplayDriver : DisplayDriver
          // *** ViewPort ***
          glViewport(x, y, w, h);
 
+         GLMatrixMode(MatrixMode::texture);
+         if(!display.display3D.camera)
+            GLPushMatrix();
+         GLLoadIdentity();
+
          // *** Projection Matrix ***
-         glMatrixMode(MatrixMode::projection);
+         GLMatrixMode(MatrixMode::projection);
          if(!display.display3D.camera)
-            glPushMatrix();
+            GLPushMatrix();
 
          if(display.display3D.collectingHits)
          {
@@ -2851,11 +3108,11 @@ class OpenGLDisplayDriver : DisplayDriver
                   (h + 2.0f * (y - pickY)) / display.display3D.pickHeight, 0, 1
                }
             };
-            glLoadMatrixd(pickMatrix.array);
+            GLLoadMatrixd(pickMatrix.array);
          }
          else
-            glLoadIdentity();
-         glFrustum(
+            GLLoadIdentity();
+         GLFrustum(
             (left   - origX) * camera.zMin / camera.focalX,
             (right  - origX) * camera.zMin / camera.focalX,
             (bottom - origY) * camera.zMin / camera.focalY,
@@ -2865,142 +3122,599 @@ class OpenGLDisplayDriver : DisplayDriver
          glDisable(GL_BLEND);
 
          // *** Z Inverted Identity Matrix ***
-         glMatrixMode(MatrixMode::modelView);
+         GLMatrixMode(MatrixMode::modelView);
          if(!display.display3D.camera)
-            glPushMatrix();
+            GLPushMatrix();
 
-         glLoadIdentity();
+         GLLoadIdentity();
 
-         glScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
+         GLScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
 
          // *** View Matrix ***
-         glMultMatrixd(camera.viewMatrix.array);
+         GLMultMatrixd(camera.viewMatrix.array);
+
+#if ENABLE_GL_SHADERS
+         if(glCaps_shaders)
+         {
+            defaultShader.select();
+            defaultShader.setCamera(camera);
+         }
+#endif
 
          // *** Lights ***
          // ...
 
          glEnable(GL_DEPTH_TEST);
 
-#if !defined(EM_MODE) && !defined(SHADERS)
-         glEnable(GL_LIGHTING);
-         glShadeModel(GL_SMOOTH);
+         GLSetupLighting(true);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            glShadeModel(GL_SMOOTH);
 #endif
          glDepthMask((byte)bool::true);
          oglDisplay.depthWrite = true;
 
-         glEnable(GL_MULTISAMPLE_ARB);
+#ifndef __EMSCRIPTEN__
+         glEnable(GL_MULTISAMPLE);
+#endif
       }
       else if(surface && display.display3D.camera)
       {
+         nearPlane = 1;
          oglDisplay.depthWrite = false;
          glViewport(0,0,display.width,display.height);
 
          glDisable(GL_CULL_FACE);
          glDisable(GL_DEPTH_TEST);
-         GLSetupTexturing(false);
-#if !defined(EM_MODE) && !defined(SHADERS)
-         glDisable(GL_LIGHTING);
-         glDisable(GL_FOG);
-         glShadeModel(GL_FLAT);
+
+         GLDisableClientState(COLORS);
+#if ENABLE_GL_SHADERS
+         if(glCaps_shaders)
+         {
+            GLDisableClientState(TANGENTS1);
+            GLDisableClientState(TANGENTS2);
+         }
+#endif
+         GLDisableClientState(NORMALS);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            GLDisableClientState(LIGHTVECTORS);
 #endif
-         glEnable(GL_BLEND);
-         glDisable(GL_MULTISAMPLE_ARB);
 
          // *** Restore 2D MODELVIEW Matrix ***
-         glPopMatrix();
+         GLMatrixMode(MatrixMode::modelView);
+         GLPopMatrix();
+
+         // *** Restore 2D TEXTURE Matrix ***
+         GLMatrixMode(MatrixMode::texture);
+         GLPopMatrix();
 
          // *** Restore 2D PROJECTION Matrix ***
-         glMatrixMode(MatrixMode::projection);
-         glPopMatrix();
-      }
+         GLMatrixMode(MatrixMode::projection);
+         GLPopMatrix();
+
+         // NOTE: We expect the 2D projection matrix to be the active one for GetSurface to call glOrtho()
+
+#if ENABLE_GL_SHADERS
+         if(glCaps_shaders)
+            defaultShader.select();
+#endif
+
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+         {
+            disableRemainingTMUs(display, 0);
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+            glDisable(GL_TEXTURE_CUBE_MAP);
+         #if _GLES
+            glDisable(GL_TEXTURE_GEN_STR);
+         #else
+            glDisable(GL_TEXTURE_GEN_R);
+            glDisable(GL_TEXTURE_GEN_S);
+            glDisable(GL_TEXTURE_GEN_T);
+         #endif
+         }
+#endif
+
+         GLSetupTexturing(false);
+         GLSetupLighting(false);
+         GLSetupFog(false);
+
+#if ENABLE_GL_SHADERS
+         if(glCaps_shaders)
+         {
+            defaultShader.setPerVertexColor(false);
+            defaultShader.setMaterial(null, 0);
+         }
+#endif
 
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            glShadeModel(GL_FLAT);
+#endif
+         glEnable(GL_BLEND);
+#if !defined(__EMSCRIPTEN__)
+         glDisable(GL_MULTISAMPLE);
+#endif
+      }
    }
 
    void ApplyMaterial(Display display, Material material, Mesh mesh)
    {
-      //Logf("ApplyMaterial\n");
+      Shader shader = material.shader ? material.shader : defaultShader;
+      MaterialFlags flags = material.flags;
+#if ENABLE_GL_FFP
+      static int lastSeparate = 0;
+      int tmu = 0;
+      bool normalMapped = false;
+      OGLMesh oglMesh = mesh ? mesh.data : null;
+#endif
+
+#if ENABLE_GL_SHADERS
+      if(glCaps_shaders && shader)
+         shader.select();
+#endif
 
       // Basic Properties
-      if(material.flags.doubleSided)
+      if(flags.doubleSided)
       {
-#if !defined(EM_MODE) && !defined(SHADERS)
-         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, !material.flags.singleSideLight);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, !flags.singleSideLight);
 #endif
          glDisable(GL_CULL_FACE);
       }
       else
       {
-#if !defined(EM_MODE) && !defined(SHADERS)
-         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false);
 #endif
          glEnable(GL_CULL_FACE);
       }
 
-#if !defined(SHADERS)
       // Fog
-      if(material.flags.noFog)
-         glDisable(GL_FOG);
-      else
-         glEnable(GL_FOG);
+      GLSetupFog(!flags.noFog);
+
+#if ENABLE_GL_SHADERS
+      if(glCaps_shaders)
+         activeShader.setMaterial(material, mesh.flags);
 #endif
 
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
+      {
+         if(material.bumpMap && mesh.lightVectors)
+         {
+            float color[4] = { 1,1,1,1 };
+            glActiveTexture(GL_TEXTURE0 + tmu);
+            glClientActiveTexture(GL_TEXTURE0 + tmu++);
+            glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData);
+            glDisable(GL_TEXTURE_CUBE_MAP);
+            glEnable(GL_TEXTURE_2D);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB);
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+            if(0) //((DefaultShaderBits)defaultShader.state).debugging)
+            {
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR);
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
+               glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+            }
+
+         #if _GLES
+            glDisable(GL_TEXTURE_GEN_STR);
+         #else
+            glDisable(GL_TEXTURE_GEN_R);
+            glDisable(GL_TEXTURE_GEN_S);
+            glDisable(GL_TEXTURE_GEN_T);
+         #endif
+            glDisable(GL_LIGHTING);
+            lightingEnabled = false;
+
+            GLMatrixMode(GL_TEXTURE);
+            GLLoadIdentity();
+            if(material.uScale && material.vScale)
+               GLScalef(material.uScale, material.vScale, 1);
+            GLMatrixMode(MatrixMode::modelView);
+
+            if(flags.tile)
+            {
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+            }
+            else
+            {
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            }
+
+            glActiveTexture(GL_TEXTURE0 + tmu);
+            glClientActiveTexture(GL_TEXTURE0 + tmu);
+
+            normalMapped = true;
+
+            // Modulate base color
+            if(material.diffuse.r < 1 || material.diffuse.g < 1 || material.diffuse.b < 1)
+            {
+               tmu++;
+               glDisable(GL_TEXTURE_CUBE_MAP);
+               glEnable(GL_TEXTURE_2D);
+               glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData);
+               glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+               color[0] = material.diffuse.r, color[1] = material.diffuse.g, color[2] = material.diffuse.b, color[3] = 1.0;
+               glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+               glActiveTexture(GL_TEXTURE0 + tmu);
+               glClientActiveTexture(GL_TEXTURE0 + tmu);
+            }
+
+            // Add ambient light
+            {
+               ColorRGB ambient { material.ambient.r * 0.2f, material.ambient.g * 0.2f, material.ambient.g * 0.2f };
+               if(ambient.r > 0 || ambient.g > 0 || ambient.b > 0)
+               {
+                  tmu++;
+                  glDisable(GL_TEXTURE_CUBE_MAP);
+                  glEnable(GL_TEXTURE_2D);
+                  glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)material.bumpMap.driverData);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+                  glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+                  glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+                  glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+                  color[0] = ambient.r, color[1] = ambient.g, color[2] = ambient.b, color[3] = 1.0;
+                  glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+                  glActiveTexture(GL_TEXTURE0 + tmu);
+                  glClientActiveTexture(GL_TEXTURE0 + tmu);
+               }
+            }
+         }
+         else
+         {
+            GLDisableClientState(LIGHTVECTORS);
+            if(!lightingEnabled)
+            {
+               glEnable(GL_LIGHTING);
+               lightingEnabled = true;
+            }
+         }
+      }
+#endif
       // Maps
-      if(material.baseMap && (mesh.texCoords || mesh.flags.texCoords1))
+      if(flags.cubeMap || (material.baseMap && (mesh.texCoords || mesh.flags.texCoords1)))
       {
          Bitmap map = material.baseMap;
-         GLSetupTexturing(true);
-         glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)map.driverData);
+         int diffuseTarget = flags.cubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
 
-         glMatrixMode(GL_TEXTURE);
-         glLoadIdentity();
-         if(material.uScale && material.vScale)
-            glScalef(material.uScale, material.vScale, 1);
-         glMatrixMode(MatrixMode::modelView);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+         {
+            glActiveTexture(GL_TEXTURE0 + tmu);
+            glClientActiveTexture(GL_TEXTURE0 + tmu++);
+            glEnable(diffuseTarget);
+            glDisable(flags.cubeMap ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP);
+         }
+#endif
+
+#if ENABLE_GL_SHADERS
+         if(glCaps_shaders && !flags.cubeMap)
+            GLSetupTexturing(true);
+#endif
+
+         glBindTexture(diffuseTarget, (GLuint)(uintptr)map.driverData);
+
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+         {
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+            /* // This did not have the desired effect with a GL_ALPHA texture
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+            */
+
+            if(flags.cubeMap)
+            {
+            #if _GLES
+               glEnable(GL_TEXTURE_GEN_STR);
+               // GL_OBJECT_LINEAR: No extension support?
+               // glTexGeni(GL_TEXTURE_GEN_STR_OES, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+            #else
+               GLfloat xPlane[] = { 1.0f, 0.0f, 0.0f, 0 };
+               GLfloat yPlane[] = { 0.0f,-1.0f, 0.0f, 0 };
+               GLfloat zPlane[] = { 0.0f, 0.0f,-1.0f, 0 };
+               glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+               glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+               glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+               glTexGenfv(GL_R, GL_OBJECT_PLANE, zPlane);
+               glTexGenfv(GL_S, GL_OBJECT_PLANE, xPlane);
+               glTexGenfv(GL_T, GL_OBJECT_PLANE, yPlane);
+
+               glEnable(GL_TEXTURE_GEN_R);
+               glEnable(GL_TEXTURE_GEN_S);
+               glEnable(GL_TEXTURE_GEN_T);
+            #endif
+
+               GLDisableClientState(TEXCOORDS);
+            }
+            else
+            {
+            #if _GLES
+               glDisable(GL_TEXTURE_GEN_STR);
+            #else
+               glDisable(GL_TEXTURE_GEN_R);
+               glDisable(GL_TEXTURE_GEN_S);
+               glDisable(GL_TEXTURE_GEN_T);
+            #endif
 
-         if(material.flags.tile)
+               if(tmu > 1)
+                  oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? null : mesh.texCoords);
+               GLEnableClientState(TEXCOORDS);
+            }
+            glClientActiveTexture(GL_TEXTURE0);
+         }
+#endif
+         if(flags.tile)
          {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
          }
          else
          {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
          }
       }
       else
+      {
          GLSetupTexturing(false);
+      }
 
-#if defined(EM_MODE) || defined(SHADERS)
-      glimtkColor4f(material.diffuse.r, material.diffuse.g, material.diffuse.b, material.opacity);
-#else
-      if(mesh.flags.colors)
+#if ENABLE_GL_FFP && !defined(_GLES)
+      if(!glCaps_shaders)
       {
-         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
-         glEnable(GL_COLOR_MATERIAL);
+         int separate = material.flags.separateSpecular ? GL_SEPARATE_SPECULAR_COLOR : GL_SINGLE_COLOR;
+         if(separate != lastSeparate)
+         {
+            GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, separate);
+            lastSeparate = separate;
+         }
       }
-      else
+#endif
+
+      if((flags.cubeMap && material.baseMap) ||
+         (mesh.texCoords && (material.baseMap || material.bumpMap || material.specularMap || material.reflectMap)))
       {
-         glDisable(GL_COLOR_MATERIAL);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
          {
-            float color[4] = { material.diffuse.r, material.diffuse.g, material.diffuse.b, material.opacity };
-            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
+            glActiveTexture(GL_TEXTURE0 + tmu - 1);
+            glClientActiveTexture(GL_TEXTURE0 + tmu - 1);
          }
+#endif
+         GLMatrixMode(GL_TEXTURE);
+         GLLoadIdentity();
+         if(material.uScale && material.vScale)
+            GLScalef(material.uScale, material.vScale, 1);
+         GLMatrixMode(MatrixMode::modelView);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
          {
-            float color[4] = { material.ambient.r, material.ambient.g, material.ambient.b, 0 };
-            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
+            glActiveTexture(GL_TEXTURE0);
+            glClientActiveTexture(GL_TEXTURE0);
          }
+#endif
       }
+
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
       {
-         float color[4] = { material.specular.r, material.specular.g, material.specular.b, 0 };
-         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
+         if(material.envMap && material.refractiveIndex)
+         {
+            float color[4] = { material.opacity, material.opacity, material.opacity, 1.0 };
+            glActiveTexture(GL_TEXTURE0 + tmu);
+            glClientActiveTexture(GL_TEXTURE0 + tmu++);
+            glBindTexture(GL_TEXTURE_CUBE_MAP, (GLuint)(uintptr)material.envMap.driverData);
+            glEnable(GL_TEXTURE_CUBE_MAP);
+         #if _GLES
+            glEnable(GL_TEXTURE_GEN_STR);
+            glTexGeni(GL_TEXTURE_GEN_STR_OES, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+         #else
+            glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+            glEnable(GL_TEXTURE_GEN_R);
+            glEnable(GL_TEXTURE_GEN_S);
+            glEnable(GL_TEXTURE_GEN_T);
+         #endif
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+            if(normalMapped)
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+            else
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+            if(!normalMapped)
+            {
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, GL_SRC_ALPHA);
+            }
+
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+
+            GLMatrixMode(MatrixMode::texture);
+            {
+               double * s = display.display3D.camera.viewMatrix.array;
+               double k = 2.0;
+               Matrix m
+               { {
+                  k*s[0],-k*s[4],-k*s[8], 0,
+                  k*s[1],-k*s[5],-k*s[9], 0,
+                  k*s[2],-k*s[6],-k*s[10],0,
+                  0,0,0,1
+               } };
+               GLLoadMatrixd(m.array);
+            }
+            GLMatrixMode(MatrixMode::modelView);
+         }
+
+         if(material.envMap && material.reflectivity)
+         {
+            float color[4] = { 1.0f - material.reflectivity, 1.0f - material.reflectivity, 1.0f - material.reflectivity, 1.0 };
+            glActiveTexture(GL_TEXTURE0 + tmu);
+            glClientActiveTexture(GL_TEXTURE0 + tmu++);
+            glBindTexture(GL_TEXTURE_CUBE_MAP, (GLuint)(uintptr)material.envMap.driverData);
+            glEnable(GL_TEXTURE_CUBE_MAP);
+         #if _GLES
+            glEnable(GL_TEXTURE_GEN_STR);
+            glTexGeni(GL_TEXTURE_GEN_STR_OES, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+         #else
+            glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+            glEnable(GL_TEXTURE_GEN_R);
+            glEnable(GL_TEXTURE_GEN_S);
+            glEnable(GL_TEXTURE_GEN_T);
+         #endif
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+            if(normalMapped)
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+            else
+               glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+            if(!normalMapped)
+            {
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_CONSTANT);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
+               glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, GL_SRC_ALPHA);
+            }
+
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
+
+            GLMatrixMode(MatrixMode::texture);
+            {
+               double * s = display.display3D.camera.inverseTranspose.array;
+               Matrix m
+               { {
+                  s[0],s[1],-s[2],0,
+                  s[4],-s[5],-s[6],0,
+                  -s[8],s[9],s[10],0,
+                  0,0,0,1
+               } };
+               GLLoadMatrixd(m.array);
+            }
+            GLMatrixMode(MatrixMode::modelView);
+         }
       }
+#endif
+
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
       {
-         float color[4] = { material.emissive.r, material.emissive.g, material.emissive.b, 0 };
-         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color);
-      }
+         disableRemainingTMUs(display, tmu);
 
-      glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &material.power);
+         if(mesh.flags.colors)
+         {
+            GLColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
+            glEnable(GL_COLOR_MATERIAL);
+         }
+         else
+         {
+            glDisable(GL_COLOR_MATERIAL);
+            {
+               float color[4] = { material.diffuse.r, material.diffuse.g, material.diffuse.b, material.opacity };
+               glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
+            }
+            {
+               float color[4] = { material.ambient.r, material.ambient.g, material.ambient.b, 0 };
+               glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
+            }
+         }
+         if(material.power > 0.1)
+         {
+            float color[4] = { material.specular.r, material.specular.g, material.specular.b, 0 };
+            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
+         }
+         else
+         {
+            float color[4] = { 0,0,0,0 };
+            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
+         }
+         {
+            float color[4] = { material.emissive.r, material.emissive.g, material.emissive.b, 0 };
+            glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color);
+         }
+         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &material.power);
+      }
 #endif
    }
 
@@ -3009,6 +3723,10 @@ class OpenGLDisplayDriver : DisplayDriver
       OGLMesh oglMesh = mesh.data;
       if(oglMesh)
       {
+         OGLSystem oglSystem = displaySystem.driverData;
+         GLCapabilities caps = glCaps;
+         SETCAPS(oglSystem.capabilities);
+
          if(!mesh.flags.vertices)
          {
             oglMesh.vertices.free();
@@ -3019,6 +3737,16 @@ class OpenGLDisplayDriver : DisplayDriver
             oglMesh.normals.free();
             delete mesh.normals;
          }
+         if(!mesh.flags.tangents)
+         {
+            oglMesh.tangents.free();
+            delete mesh.tangents;
+         }
+         if(!mesh.flags.lightVectors)
+         {
+            oglMesh.lightVectors.free();
+            delete mesh.lightVectors;
+         }
          if(!mesh.flags.texCoords1)
          {
             oglMesh.texCoords.free();
@@ -3039,6 +3767,7 @@ class OpenGLDisplayDriver : DisplayDriver
             delete oglMesh;
             mesh.data = null;
          }
+         SETCAPS(caps);
       }
    }
 
@@ -3073,6 +3802,14 @@ class OpenGLDisplayDriver : DisplayDriver
                   else
                      mesh.normals = new Vector3Df[nVertices];
                }
+               if(!mesh.flags.tangents && flags.tangents)
+               {
+                  mesh.tangents = new Vector3Df[2*nVertices];
+               }
+               if(!mesh.flags.lightVectors && flags.lightVectors)
+               {
+                  mesh.lightVectors = new ColorRGB[nVertices];
+               }
                if(!mesh.flags.texCoords1 && flags.texCoords1)
                {
                   mesh.texCoords = new Pointf[nVertices];
@@ -3113,6 +3850,14 @@ class OpenGLDisplayDriver : DisplayDriver
             {
                mesh.colors = renew mesh.colors ColorRGBAf[nVertices];
             }
+            if(flags.tangents)
+            {
+               mesh.tangents = renew mesh.tangents Vector3Df[2 * nVertices];
+            }
+            if(flags.lightVectors)
+            {
+               mesh.lightVectors = renew mesh.lightVectors ColorRGB[nVertices];
+            }
          }
          result = true;
       }
@@ -3121,27 +3866,37 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void UnlockMesh(DisplaySystem displaySystem, Mesh mesh, MeshFeatures flags)
    {
-      OGLMesh oglMesh = mesh.data;
-      if(!flags) flags = mesh.flags;
+      OGLSystem oglSystem = displaySystem.driverData;
+      GLCapabilities caps = glCaps;
+      SETCAPS(oglSystem.capabilities);
 
-      if(vboAvailable)
+      if(glCaps_vertexBuffer)
       {
+         OGLMesh oglMesh = mesh.data;
+         if(!flags) flags = mesh.flags;
          if(flags.vertices)
-            oglMesh.vertices.upload(
-               mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices); //, GL_STATIC_DRAW_ARB );
+            oglMesh.vertices.allocate(
+               mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices, staticDraw);
 
          if(flags.normals)
-            oglMesh.normals.upload(
-               mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals); //, GL_STATIC_DRAW_ARB );
+            oglMesh.normals.allocate(
+               mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals, staticDraw);
 
          if(flags.texCoords1)
-            oglMesh.texCoords.upload(
-               mesh.nVertices * sizeof(Pointf), mesh.texCoords); //, GL_STATIC_DRAW_ARB );
+            oglMesh.texCoords.allocate(
+               mesh.nVertices * sizeof(Pointf), mesh.texCoords, staticDraw);
 
          if(flags.colors)
-            oglMesh.colors.upload(
-               mesh.nVertices * sizeof(ColorRGBAf), mesh.colors); //, GL_STATIC_DRAW_ARB );
+            oglMesh.colors.allocate(
+               mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, staticDraw);
+
+         if(flags.tangents)
+            oglMesh.tangents.allocate(mesh.nVertices * 2*sizeof(Vector3Df), mesh.tangents, staticDraw);
+
+         if(flags.lightVectors)
+            oglMesh.lightVectors.allocate(mesh.nVertices * sizeof(ColorRGB), mesh.lightVectors, staticDraw);
       }
+      SETCAPS(caps);
    }
 
    bool LockMesh(DisplaySystem displaySystem, Mesh mesh, MeshFeatures flags)
@@ -3153,12 +3908,17 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void FreeIndices(DisplaySystem displaySystem, OGLIndices oglIndices)
    {
+      OGLSystem oglSystem = displaySystem.driverData;
+      GLCapabilities caps = glCaps;
+      SETCAPS(oglSystem.capabilities);
+
       if(oglIndices)
       {
          oglIndices.buffer.free();
          delete oglIndices.indices;
          delete oglIndices;
       }
+      SETCAPS(caps);
    }
 
    void * AllocateIndices(DisplaySystem displaySystem, int nIndices, bool indices32bit)
@@ -3174,23 +3934,41 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void UnlockIndices(DisplaySystem displaySystem, OGLIndices oglIndices, bool indices32bit, int nIndices)
    {
-      if(vboAvailable)
+      OGLSystem oglSystem = displaySystem.driverData;
+      GLCapabilities caps = glCaps;
+      SETCAPS(oglSystem.capabilities);
+
+      if(glCaps_vertexBuffer)
       {
-#ifdef ES1_1
-         if(indices32bit)
+         if(!glCaps_intAndDouble && indices32bit)
          {
             if(!oglIndices.buffer.buffer)
                glGenBuffers(1, &oglIndices.buffer.buffer);
             if(glabCurElementBuffer != oglIndices.buffer.buffer)
                GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oglIndices.buffer.buffer);
-            glimtkBufferDatai(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * nIndices, oglIndices.indices, GL_STATIC_DRAW_ARB);
+
+            {
+               uint * pointer = (uint *)oglIndices.indices;
+               int i;
+               uint16 * b;
+               if(nIndices > oglSystem.shortBDSize)
+               {
+                  oglSystem.shortBDSize = nIndices;
+                  oglSystem.shortBDBuffer = renew oglSystem.shortBDBuffer uint16[oglSystem.shortBDSize];
+               }
+               b = oglSystem.shortBDBuffer;
+               for(i = 0; i < nIndices; i++)
+                  b[i] = (uint16)pointer[i];
+
+               glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndices * sizeof(uint16), b, GL_STATIC_DRAW);
+            }
          }
          else
-#endif
-         oglIndices.buffer.upload(
-            nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)),
-            oglIndices.indices); //GL_STATIC_DRAW_ARB);
+            oglIndices.buffer.allocate(
+               nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)),
+               oglIndices.indices, staticDraw);
       }
+      SETCAPS(caps);
    }
 
    uint16 * LockIndices(DisplaySystem displaySystem, OGLIndices oglIndices)
@@ -3201,23 +3979,19 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void SelectMesh(Display display, Mesh mesh)
    {
-      //Logf("SelectMesh\n");
-
 #if !defined( __ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
-
 #if defined(__WIN32__)
       if(glUnlockArraysEXT)
 #endif
-         if(!vboAvailable && display.display3D.mesh)
+         if(!glCaps_vertexBuffer && display.display3D.mesh)
             glUnlockArraysEXT();
-
 #endif
       if(mesh)
       {
          OGLMesh oglMesh = mesh.data;
 
          // *** Vertex Stream ***
-         glEnableClientState(GL_VERTEX_ARRAY);
+         GLEnableClientState(VERTICES);
          if(!display.display3D.collectingHits && oglMesh)
          {
             oglMesh.vertices.use(vertex, 3, (mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT), 0, oglMesh.vertices.buffer ? null : (double *)mesh.vertices);
@@ -3225,54 +3999,119 @@ class OpenGLDisplayDriver : DisplayDriver
             // *** Normals Stream ***
             if(mesh.normals || mesh.flags.normals)
             {
-               glEnableClientState(GL_NORMAL_ARRAY);
+               GLEnableClientState(NORMALS);
                oglMesh.normals.use(normal, 3, GL_FLOAT, 0, oglMesh.normals.buffer ? null : mesh.normals);
             }
             else
-               glDisableClientState(GL_NORMAL_ARRAY);
+               GLDisableClientState(NORMALS);
+
+#if ENABLE_GL_SHADERS
+            if(glCaps_shaders)
+            {
+               // *** Tangents Stream ***
+               if(mesh.tangents || mesh.flags.tangents)
+               {
+                  GLEnableClientState(TANGENTS1);
+                  GLEnableClientState(TANGENTS2);
+                  oglMesh.tangents.use(tangent1, 3, GL_FLOAT, sizeof(Vector3Df)*2, oglMesh.tangents.buffer ? null : mesh.tangents);
+                  oglMesh.tangents.use(tangent2, 3, GL_FLOAT, sizeof(Vector3Df)*2, oglMesh.tangents.buffer ? (void *)sizeof(Vector3Df) : mesh.tangents+1);
+               }
+               else
+               {
+                  GLDisableClientState(TANGENTS1);
+                  GLDisableClientState(TANGENTS2);
+               }
+            }
+#endif
 
             // *** Texture Coordinates Stream ***
             if(mesh.texCoords || mesh.flags.texCoords1)
             {
-               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+               GLEnableClientState(TEXCOORDS);
                oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? null : mesh.texCoords);
             }
             else
-               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+               GLDisableClientState(TEXCOORDS);
 
+#if ENABLE_GL_FFP
+            if(!glCaps_shaders)
+            {
+               // *** Normal Map Aligned Light Vector ***
+               if(mesh.lightVectors || mesh.flags.lightVectors)
+               {
+                  GLEnableClientState(LIGHTVECTORS);
+                  oglMesh.lightVectors.use(lightVector, 3, GL_FLOAT, 0, oglMesh.lightVectors.buffer ? null : mesh.lightVectors);
+               }
+               else
+                  GLDisableClientState(LIGHTVECTORS);
+            }
+            else
+#endif
             // *** Color Stream ***
             if(mesh.colors || mesh.flags.colors)
             {
-               glEnableClientState(GL_COLOR_ARRAY);
+               GLEnableClientState(COLORS);
                oglMesh.colors.use(color, 4, GL_FLOAT, 0, oglMesh.colors.buffer ? null : mesh.colors);
             }
             else
-               glDisableClientState(GL_COLOR_ARRAY);
+               GLDisableClientState(COLORS);
          }
          else
          {
             noAB.use(vertex, 3, (mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT), 0, (double *)mesh.vertices);
             if((mesh.normals || mesh.flags.normals) && !display.display3D.collectingHits)
             {
-               glEnableClientState(GL_NORMAL_ARRAY);
+               GLEnableClientState(NORMALS);
                noAB.use(normal, 3, GL_FLOAT, 0, mesh.normals);
             }
             else
-               glDisableClientState(GL_NORMAL_ARRAY);
+               GLDisableClientState(NORMALS);
+#if ENABLE_GL_SHADERS
+            if(glCaps_shaders)
+            {
+               if((mesh.tangents || mesh.flags.tangents) && !display.display3D.collectingHits)
+               {
+                  GLEnableClientState(TANGENTS1);
+                  GLEnableClientState(TANGENTS2);
+                  noAB.use(tangent1, 3, GL_FLOAT, sizeof(Vector3Df)*2, mesh.tangents);
+                  noAB.use(tangent2, 3, GL_FLOAT, sizeof(Vector3Df)*2, mesh.tangents+1);
+               }
+               else
+               {
+                  GLDisableClientState(TANGENTS1);
+                  GLDisableClientState(TANGENTS2);
+               }
+            }
+#endif
+
             if((mesh.texCoords || mesh.flags.texCoords1) && !display.display3D.collectingHits)
             {
-               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+               GLEnableClientState(TEXCOORDS);
                noAB.use(texCoord, 2, GL_FLOAT, 0, mesh.texCoords);
             }
             else
-               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+               GLDisableClientState(TEXCOORDS);
+
+#if ENABLE_GL_FFP
+            if(!glCaps_shaders)
+            {
+               if((mesh.lightVectors || mesh.flags.lightVectors) && !display.display3D.collectingHits)
+               {
+                  GLEnableClientState(LIGHTVECTORS);
+                  noAB.use(lightVector, 3, GL_FLOAT, sizeof(ColorRGB), mesh.lightVectors);
+               }
+               else
+                  GLDisableClientState(LIGHTVECTORS);
+            }
+            else
+#endif
             if((mesh.colors || mesh.flags.colors) && !display.display3D.collectingHits)
             {
-               glEnableClientState(GL_COLOR_ARRAY);
+               GLEnableClientState(COLORS);
                noAB.use(color, 4, GL_FLOAT, 0, mesh.colors);
             }
             else
-               glDisableClientState(GL_COLOR_ARRAY);
+               GLDisableClientState(COLORS);
          }
 
 #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
@@ -3280,67 +4119,49 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__)
          if(glLockArraysEXT)
 #endif
-            if(!vboAvailable)
+            if(!glCaps_vertexBuffer)
                glLockArraysEXT(0, mesh.nVertices);
-
 #endif
       }
    }
 
    void DrawPrimitives(Display display, PrimitiveSingle * primitive, Mesh mesh)
    {
-      //Logf("DrawPrimitives\n");
-
       if(primitive->type.vertexRange)
-         glDrawArrays(primitiveTypes[primitive->type.primitiveType], primitive->first, primitive->nVertices);
+      {
+         GLFlushMatrices();
+         glDrawArrays(getPrimitiveType(primitive->type.primitiveType), primitive->first, primitive->nVertices);
+      }
       else
       {
-         //    *** Hoping the data won't be uploaded at all (Won't really work if another group of the mesh is using the mesh ) ***
-         // HACK TO SPEED THINGS UP...
-#ifndef __ANDROID__
-         /*GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
-         if(primitive->nIndices < (mesh.nVertices >> 2) && !primitive->type.indices32bit)
+         OGLIndices oglIndices = primitive->data;
+         GLEAB eab = ((!display.display3D.collectingHits && oglIndices && glCaps_vertexBuffer) ? oglIndices.buffer : noEAB);
+         if(!glCaps_intAndDouble && !glCaps_vertexBuffer && primitive->type.indices32bit)
          {
-            int c;
-            glBegin((GLIMTKMode)primitiveTypes[primitive->type.primitiveType]);
-            if(primitive->data)
-            {
-               OGLIndices oglIndices = primitive->data;
-               MeshFeatures flags = mesh.flags;
-               for(c = 0; c<primitive->nIndices; c++)
-               {
-                  uint16 index = ((uint16 *) oglIndices.indices)[c];
-                  if(flags.normals) glNormal3fv((float *)&mesh.normals[index]);
-                  if(flags.texCoords1) glTexCoord2fv((float *)&mesh.texCoords[index]);
-                  if(flags.colors) glColor4fv((float *)&mesh.colors[index]);
-                  glVertex3fv((float *)&mesh.vertices[index]);
-               }
-            }
-            glEnd();
+            uint16 * temp = new uint16[primitive->nIndices];
+            uint32 * src = (uint32 *)(oglIndices ? oglIndices.indices : primitive->indices);
+            int i;
+            for(i = 0; i < primitive->nIndices; i++)
+               temp[i] = (uint16)src[i];
+            eab.draw(getPrimitiveType(primitive->type.primitiveType), primitive->nIndices, GL_UNSIGNED_SHORT, temp);
+            delete temp;
          }
-         else*/
-#endif
-
-         {
-            OGLIndices oglIndices = primitive->data;
-            GLEAB eab = ((!display.display3D.collectingHits && oglIndices) ? oglIndices.buffer : noEAB);
-
-            eab.draw(primitiveTypes[primitive->type.primitiveType], primitive->nIndices,
+         else
+            eab.draw(getPrimitiveType(primitive->type.primitiveType), primitive->nIndices,
                primitive->type.indices32bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT,
                eab.buffer ? 0 : (oglIndices ? oglIndices.indices : primitive->indices));
-            GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-         }
+         GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
       }
    }
 
    void PushMatrix(Display display)
    {
-      glPushMatrix();
+      GLPushMatrix();
    }
 
    void PopMatrix(Display display, bool setMatrix)
    {
-      glPopMatrix();
+      GLPopMatrix();
    }
 
    void SetTransform(Display display, Matrix transMatrix, bool viewSpace, bool useCamera)
@@ -3350,18 +4171,18 @@ class OpenGLDisplayDriver : DisplayDriver
 
       if(viewSpace)
       {
-         glLoadIdentity();
-         glScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
+         GLLoadIdentity();
+         GLScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
       }
       else if(camera)
       {
-         glTranslated(
+         GLTranslated(
             matrix.m[3][0] - camera.cPosition.x,
             matrix.m[3][1] - camera.cPosition.y,
             matrix.m[3][2] - camera.cPosition.z);
       }
       else
-         glTranslated(
+         GLTranslated(
             matrix.m[3][0],
             matrix.m[3][1],
             matrix.m[3][2]);
@@ -3370,7 +4191,7 @@ class OpenGLDisplayDriver : DisplayDriver
       matrix.m[3][1] = 0;
       matrix.m[3][2] = 0;
 
-      glMultMatrixd(matrix.array);
+      GLMultMatrixd(matrix.array);
    }
 #endif
 }
@@ -3394,6 +4215,8 @@ IS_GLGetContext(DisplaySystem displaySystem)
 #elif defined(__ANDROID__) || defined(__ODROID__)
       return eglContext;
 #elif defined(__EMSCRIPTEN__)
+      OGLSystem system = displaySystem.driverData;
+      return (void *)system.glc;
 #else
       OGLSystem system = displaySystem.driverData;
       return system.glContext;