ecere/gfx/fontRendering: Improved rendering of some fonts e.g. MS Sans Serif having...
[sdk] / ecere / src / gfx / drivers / OpenGLDisplayDriver.ec
index 510618b..289d981 100644 (file)
-// We were using PBUFFER for alpha compositing on Linux before, but it does not seem to work, nor be required anymore.
-// #define USEPBUFFER
-
-namespace gfx::drivers;
-
-// OpenGL Extensions
-#if defined(__unix__) || defined(__APPLE__)
-
-#if !defined(__MINGW32__)
-#define GL_GLEXT_PROTOTYPES
-#endif
-
-#ifdef ECERE_MINIGLX
-
-//#include <GL/miniglx.h>
-
-#else
-
-#if defined(__ANDROID__)
-
-#else
-
-#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 uint _uint
-
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <GL/glx.h>
-#include <X11/extensions/XShm.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <X11/extensions/Xrender.h>
-#include <X11/extensions/shape.h>
-
-#undef Window
-#undef Cursor
-#undef Font
-#undef Display
-#undef Time
-#undef KeyCode
-#undef Picture
-#undef uint
-#undef new
-#undef property
-#undef class
-
-#endif
-
-#endif
-
-#endif
-
-#if defined(__APPLE__)
-#include <OpenGl/gl.h>
-#endif
-
 #if defined(__WIN32__) || defined(__unix__) || defined(__APPLE__)
 
-#if defined(__WIN32__)
-#define WIN32_LEAN_AND_MEAN
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0500
-#define String Sting_
-#include <windows.h>
-#undef String
+// #define DIAGNOSTICS
+#if defined(_DEBUG) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
+ #define GL_DEBUGGING
 #endif
 
-#if defined(__ANDROID__)
-
-#include <GLES/gl.h>
-#include <EGL/egl.h>
-
-#else
+import "Display"
 
-#include <GL/gl.h>
-#include <GL/glext.h>
+import "glab"
+import "immediate"
+import "matrixStack"
+import "defaultShader"
 
-#endif
+namespace gfx::drivers;
 
-import "Display"
+#include "gl123es.h"
 
+// **********   GL PLATFORMS INCLUDES   **********
+// UNIX
 #if defined(__unix__) || defined(__APPLE__)
 
-#ifndef __ANDROID__
-import "XInterface"
-#endif
-
-#endif
+   // EGL
+   #if defined(__ANDROID__) || defined(__ODROID__)
+      import "egl"
 
-static double nearPlane = 1;
+      #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
 
-#define glLoadMatrix glLoadMatrixd
-#define glMultMatrix glMultMatrixd
-#define glGetMatrix  glGetDoublev
-#define glTranslate glTranslated
-#define glScale glScaled
+   // Emscripten
+   #elif defined(__EMSCRIPTEN__)
+      #define property _property
+      #define uint _uint
+
+      #include <emscripten/emscripten.h>
+      #include <emscripten/html5.h>
+
+      #undef property
+      #undef uint
+
+   // GLX
+   #elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+      #define pointer _pointer
+      #define GL_GLEXT_PROTOTYPES
+
+      #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 Glyph     X11Glyph
+      #define uint _uint
+
+      #include <X11/Xlib.h>
+      #include <X11/Xutil.h>
+      #include <GL/glx.h>
+      #include <X11/extensions/XShm.h>
+      #include <sys/ipc.h>
+      #include <sys/shm.h>
+      #include <X11/extensions/Xrender.h>
+      #include <X11/extensions/shape.h>
+
+      #undef Window
+      #undef Cursor
+      #undef Font
+      #undef Display
+      #undef Time
+      #undef KeyCode
+      #undef Picture
+      #undef Glyph
+      #undef uint
+      #undef new
+      #undef property
+      #undef class
+      #undef pointer
+
+      #if !defined(__APPLE__)
+      default GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count);
+      default GLAPI void APIENTRY glUnlockArraysEXT (void);
+      #endif
 
-/*
-#define glVertex3v glVertex3dv
-#define glNormal3v glNormal3dv
-*/
+      import "XInterface"
 
-/*
-//#ifdef VERTEX_FORMAT_DOUBLE
+      // We were using PBUFFER for alpha compositing on Linux before, but it does not seem to work, nor be required anymore.
+      // #define USEPBUFFER
 
-#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
+   #endif
 
-#else
+// Apple
+#elif defined(__APPLE__)
+   #include <OpenGl/gl.h>
 
-#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
+// WGL
+#elif defined(__WIN32__)
+   //#define WIN32_LEAN_AND_MEAN
+   #undef _WIN32_WINNT
+   #define _WIN32_WINNT 0x0502
+   #define String Sting_
+   #include <windows.h>
+   #undef String
 
+   #include "wglDefs.h"
 #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__)
+#elif !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
+default:
+private:
+#endif
 
-#define WGL_SAMPLE_BUFFERS_ARB              0x2041
-#define WGL_SAMPLES_ARB                     0x2042
-
-#define  WGL_WGLEXT_VERSION   1
-#define  WGL_FRONT_COLOR_BUFFER_BIT_ARB   0x00000001
-#define  WGL_BACK_COLOR_BUFFER_BIT_ARB   0x00000002
-#define  WGL_DEPTH_BUFFER_BIT_ARB   0x00000004
-#define  WGL_STENCIL_BUFFER_BIT_ARB   0x00000008
-#define  WGL_NUMBER_PIXEL_FORMATS_ARB   0x2000
-#define  WGL_DRAW_TO_WINDOW_ARB   0x2001
-#define  WGL_DRAW_TO_BITMAP_ARB   0x2002
-#define  WGL_ACCELERATION_ARB   0x2003
-#define  WGL_NEED_PALETTE_ARB   0x2004
-#define  WGL_NEED_SYSTEM_PALETTE_ARB   0x2005
-#define  WGL_SWAP_LAYER_BUFFERS_ARB   0x2006
-#define  WGL_SWAP_METHOD_ARB   0x2007
-#define  WGL_NUMBER_OVERLAYS_ARB   0x2008
-#define  WGL_NUMBER_UNDERLAYS_ARB   0x2009
-#define  WGL_TRANSPARENT_ARB   0x200A
-#define  WGL_TRANSPARENT_RED_VALUE_ARB   0x2037
-#define  WGL_TRANSPARENT_GREEN_VALUE_ARB   0x2038
-#define  WGL_TRANSPARENT_BLUE_VALUE_ARB   0x2039
-#define  WGL_TRANSPARENT_ALPHA_VALUE_ARB   0x203A
-#define  WGL_TRANSPARENT_INDEX_VALUE_ARB   0x203B
-#define  WGL_SHARE_DEPTH_ARB   0x200C
-#define  WGL_SHARE_STENCIL_ARB   0x200D
-#define  WGL_SHARE_ACCUM_ARB   0x200E
-#define  WGL_SUPPORT_GDI_ARB   0x200F
-#define  WGL_SUPPORT_OPENGL_ARB   0x2010
-#define  WGL_DOUBLE_BUFFER_ARB   0x2011
-#define  WGL_STEREO_ARB   0x2012
-#define  WGL_PIXEL_TYPE_ARB   0x2013
-#define  WGL_COLOR_BITS_ARB   0x2014
-#define  WGL_RED_BITS_ARB   0x2015
-#define  WGL_RED_SHIFT_ARB   0x2016
-#define  WGL_GREEN_BITS_ARB   0x2017
-#define  WGL_GREEN_SHIFT_ARB   0x2018
-#define  WGL_BLUE_BITS_ARB   0x2019
-#define  WGL_BLUE_SHIFT_ARB   0x201A
-#define  WGL_ALPHA_BITS_ARB   0x201B
-#define  WGL_ALPHA_SHIFT_ARB   0x201C
-#define  WGL_ACCUM_BITS_ARB   0x201D
-#define  WGL_ACCUM_RED_BITS_ARB   0x201E
-#define  WGL_ACCUM_GREEN_BITS_ARB   0x201F
-#define  WGL_ACCUM_BLUE_BITS_ARB   0x2020
-#define  WGL_ACCUM_ALPHA_BITS_ARB   0x2021
-#define  WGL_DEPTH_BITS_ARB   0x2022
-#define  WGL_STENCIL_BITS_ARB   0x2023
-#define  WGL_AUX_BUFFERS_ARB   0x2024
-#define  WGL_NO_ACCELERATION_ARB   0x2025
-#define  WGL_GENERIC_ACCELERATION_ARB   0x2026
-#define  WGL_FULL_ACCELERATION_ARB   0x2027
-#define  WGL_SWAP_EXCHANGE_ARB   0x2028
-#define  WGL_SWAP_COPY_ARB   0x2029
-#define  WGL_SWAP_UNDEFINED_ARB   0x202A
-#define  WGL_TYPE_RGBA_ARB   0x202B
-#define  WGL_TYPE_COLORINDEX_ARB   0x202C
-#define  ERROR_INVALID_PIXEL_TYPE_ARB   0x2043
-#define  ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB   0x2054
-#define  WGL_DRAW_TO_PBUFFER_ARB   0x202D
-#define  WGL_MAX_PBUFFER_PIXELS_ARB   0x202E
-#define  WGL_MAX_PBUFFER_WIDTH_ARB   0x202F
-#define  WGL_MAX_PBUFFER_HEIGHT_ARB   0x2030
-#define  WGL_PBUFFER_LARGEST_ARB   0x2033
-#define  WGL_PBUFFER_WIDTH_ARB   0x2034
-#define  WGL_PBUFFER_HEIGHT_ARB   0x2035
-#define  WGL_PBUFFER_LOST_ARB   0x2036
-#define  ERROR_INVALID_PIXEL_TYPE_EXT   0x2043
-#define  WGL_NUMBER_PIXEL_FORMATS_EXT   0x2000
-#define  WGL_DRAW_TO_WINDOW_EXT   0x2001
-#define  WGL_DRAW_TO_BITMAP_EXT   0x2002
-#define  WGL_ACCELERATION_EXT   0x2003
-#define  WGL_NEED_PALETTE_EXT   0x2004
-#define  WGL_NEED_SYSTEM_PALETTE_EXT   0x2005
-#define  WGL_SWAP_LAYER_BUFFERS_EXT   0x2006
-#define  WGL_SWAP_METHOD_EXT   0x2007
-#define  WGL_NUMBER_OVERLAYS_EXT   0x2008
-#define  WGL_NUMBER_UNDERLAYS_EXT   0x2009
-#define  WGL_TRANSPARENT_EXT   0x200A
-#define  WGL_TRANSPARENT_VALUE_EXT   0x200B
-#define  WGL_SHARE_DEPTH_EXT   0x200C
-#define  WGL_SHARE_STENCIL_EXT   0x200D
-#define  WGL_SHARE_ACCUM_EXT   0x200E
-#define  WGL_SUPPORT_GDI_EXT   0x200F
-#define  WGL_SUPPORT_OPENGL_EXT   0x2010
-#define  WGL_DOUBLE_BUFFER_EXT   0x2011
-#define  WGL_STEREO_EXT   0x2012
-#define  WGL_PIXEL_TYPE_EXT   0x2013
-#define  WGL_COLOR_BITS_EXT   0x2014
-#define  WGL_RED_BITS_EXT   0x2015
-#define  WGL_RED_SHIFT_EXT   0x2016
-#define  WGL_GREEN_BITS_EXT   0x2017
-#define  WGL_GREEN_SHIFT_EXT   0x2018
-#define  WGL_BLUE_BITS_EXT   0x2019
-#define  WGL_BLUE_SHIFT_EXT   0x201A
-#define  WGL_ALPHA_BITS_EXT   0x201B
-#define  WGL_ALPHA_SHIFT_EXT   0x201C
-#define  WGL_ACCUM_BITS_EXT   0x201D
-#define  WGL_ACCUM_RED_BITS_EXT   0x201E
-#define  WGL_ACCUM_GREEN_BITS_EXT   0x201F
-#define  WGL_ACCUM_BLUE_BITS_EXT   0x2020
-#define  WGL_ACCUM_ALPHA_BITS_EXT   0x2021
-#define  WGL_DEPTH_BITS_EXT   0x2022
-#define  WGL_STENCIL_BITS_EXT   0x2023
-#define  WGL_AUX_BUFFERS_EXT   0x2024
-#define  WGL_NO_ACCELERATION_EXT   0x2025
-#define  WGL_GENERIC_ACCELERATION_EXT   0x2026
-#define  WGL_FULL_ACCELERATION_EXT   0x2027
-#define  WGL_SWAP_EXCHANGE_EXT   0x2028
-#define  WGL_SWAP_COPY_EXT   0x2029
-#define  WGL_SWAP_UNDEFINED_EXT   0x202A
-#define  WGL_TYPE_RGBA_EXT   0x202B
-#define  WGL_TYPE_COLORINDEX_EXT   0x202C
-#define  WGL_DRAW_TO_PBUFFER_EXT   0x202D
-#define  WGL_MAX_PBUFFER_PIXELS_EXT   0x202E
-#define  WGL_MAX_PBUFFER_WIDTH_EXT   0x202F
-#define  WGL_MAX_PBUFFER_HEIGHT_EXT   0x2030
-#define  WGL_OPTIMAL_PBUFFER_WIDTH_EXT   0x2031
-#define  WGL_OPTIMAL_PBUFFER_HEIGHT_EXT   0x2032
-#define  WGL_PBUFFER_LARGEST_EXT   0x2033
-#define  WGL_PBUFFER_WIDTH_EXT   0x2034
-#define  WGL_PBUFFER_HEIGHT_EXT   0x2035
-#define  WGL_DEPTH_FLOAT_EXT   0x2040
-#define  WGL_SAMPLE_BUFFERS_3DFX   0x2060
-#define  WGL_SAMPLES_3DFX   0x2061
-#define  WGL_SAMPLE_BUFFERS_EXT   0x2041
-#define  WGL_SAMPLES_EXT   0x2042
-#define  WGL_GENLOCK_SOURCE_MULTIVIEW_I3D   0x2044
-#define  WGL_GENLOCK_SOURCE_EXTENAL_SYNC_I3D   0x2045
-#define  WGL_GENLOCK_SOURCE_EXTENAL_FIELD_I3D   0x2046
-#define  WGL_GENLOCK_SOURCE_EXTENAL_TTL_I3D   0x2047
-#define  WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D   0x2048
-#define  WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D   0x2049
-#define  WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D   0x204A
-#define  WGL_GENLOCK_SOURCE_EDGE_RISING_I3D   0x204B
-#define  WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D   0x204C
-#define  WGL_GAMMA_TABLE_SIZE_I3D   0x204E
-#define  WGL_GAMMA_EXCLUDE_DESKTOP_I3D   0x204F
-#define  WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D   0x2050
-#define  WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D   0x2051
-#define  WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D   0x2052
-#define  WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D   0x2053
-#define  WGL_ARB_buffer_region   1
-#define  WGL_ARB_extensions_string   1
-#define  WGL_ARB_pixel_format   1
-#define  WGL_ARB_make_current_read   1
-#define  WGL_ARB_pbuffer   1
-#define  WGL_EXT_display_color_table   1
-#define  WGL_EXT_extensions_string   1
-#define  WGL_EXT_make_current_read   1
-#define  WGL_EXT_pbuffer   1
-#define  WGL_EXT_pixel_format   1
-#define  WGL_EXT_swap_control   1
-#define  WGL_WGL_EXT_depth_float   1
-#define  WGL_WGL_3DFX_multisample   1
-#define  WGL_WGL_EXT_multisample   1
-#define  WGL_NV_allocate_memory   1
-
-/*
-typedef void (APIENTRY * PFNGLACTIVETEXTUREARBPROC) (GLenum target);
-typedef void (APIENTRY * PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t);
-typedef void (APIENTRY * PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum target);
-typedef void (APIENTRY * PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count);
-typedef void (APIENTRY * PFNGLUNLOCKARRAYSEXTPROC) (void);
-*/
-
-/*
-typedef int (APIENTRY * PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer);
-typedef int (APIENTRY * PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers);
-typedef int (APIENTRY * PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers);
-typedef int (APIENTRY * PFNGLBUFFERDATAARBPROC) (GLenum target, int size, const GLvoid *data, GLenum usage);
+/*                                                            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      |      ~      |     -      |     ~      |     -
 */
-typedef int (APIENTRY * PFNWGLCHOOSEPIXELFORMATARBPROC) ();
-typedef void * (APIENTRY * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList);
-typedef HDC (APIENTRY * PFNWGLGETPBUFFERDCARBPROC) (void * hPbuffer);
-typedef int (APIENTRY * PFNWGLRELEASEPBUFFERDCARBPROC) (void * hPbuffer, HDC hDC);
-typedef BOOL (APIENTRY * PFNWGLDESTROYPBUFFERARBPROC) (void * hPbuffer);
-typedef BOOL (APIENTRY * PFNWGLQUERYPBUFFERARBPROC) (void * hPbuffer, int iAttribute, int *piValue);
-typedef const char * (APIENTRY * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
-typedef BOOL (APIENTRY * PFNWGLBINDTEXIMAGEARBPROC) (void * hPbuffer, int iBuffer);
-typedef BOOL (APIENTRY * PFNWGLRELEASETEXIMAGEARBPROC) (void * hPbuffer, int iBuffer);
-
-static PFNGLMAPBUFFERARBPROC glMapBufferARB = null;
-static PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = null;
-static PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = null;
-static PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = null;
-static PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = null;
-static PFNGLLOCKARRAYSEXTPROC glLockArraysEXT = null;
-static PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT = null;
-static PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate = null;
-
-static PFNGLGENBUFFERSARBPROC glGenBuffersARB = null;
-static PFNGLBINDBUFFERARBPROC glBindBufferARB = null;
-static PFNGLBUFFERDATAARBPROC glBufferDataARB = null;
-static PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = null;
-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;
-
-#ifdef WGL_WGLEXT_PROTOTYPES
-extern BOOL WINAPI wglSwapIntervalEXT (int);
-extern int WINAPI wglGetSwapIntervalEXT (void);
-#endif /* WGL_WGLEXT_PROTOTYPES */
-typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval);
-typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void);
-
-static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
-
-#else
-
-#if defined(__ANDROID__)
-
-// Our own matrix stack
-static Matrix matrixStack[3][32];
-static int matrixIndex[3];
-static int curStack = 0;
-
-// OpenGL ES Porting Kit
-
-#define glBindFramebuffer        glBindFramebufferOES
-#define glBindRenderbuffer       glBindRenderbufferOES
-#define GL_FRAMEBUFFER           GL_FRAMEBUFFER_OES
-#define GL_RENDERBUFFER          GL_RENDERBUFFER_OES
-#define glFramebufferTexture2D   glFramebufferTexture2DOES
-#define GL_COLOR_ATTACHMENT0     GL_COLOR_ATTACHMENT0_OES
-#define glGenFramebuffers        glGenFramebuffersOES
-#define glGenRenderbuffers       glGenRenderbuffersOES
-#define glDeleteFramebuffers     glDeleteFramebuffersOES
-#define glDeleteRenderbuffers    glDeleteRenderbuffersOES
-
-#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 glDrawElementsi(type, count, start)  glDrawElements(type, count, GL_UNSIGNED_SHORT, start)
-
-#define GL_UNSIGNED_INT                         0x1405
-#define GL_DOUBLE                               0x140A
-
-#define GL_POLYGON      9
-#define GL_QUADS        7
-
-#define glBufferDatai         glesBufferDatai
-#define glBufferDatad         glesBufferDatad
-#define glVertexPointeri      glesVertexPointeri
-#define glVertexPointerd      glesVertexPointerd
-
-#define glRecti               glesRecti
-#define glBegin               glesBegin
-#define glTexCoord2i          glesTexCoord2i
-#define glVertex2i            glesVertex2i
-#define glTexCoord2d          glesTexCoord2d
-#define glVertex2d            glesVertex2d
-#define glTexCoord2f          glesTexCoord2f
-#define glVertex2f            glesVertex2f
-#define glEnd                 glesEnd
-#define glColor3f             glesColor3f
-#define glColor4ub            glesColor4ub
-#define glColor4fv            glesColor4fv
-#define glLineStipple         glesLineStipple
-#define glNormal3fv           glesNormal3fv
-#define glTexCoord2fv         glesTexCoord2fv
-#define glColorMaterial       glesColorMaterial
-
-#define glLoadMatrixd         glesLoadMatrixd
-#define glMultMatrixd         glesMultMatrixd
-#define glFrustum             glesFrustum
-#define glOrtho               glesOrtho
-#define glScaled              glesScaled
-#define glTranslated          glesTranslated
-#define glRotated             glesRotated
-#define glVertex3d            glesVertex3d
-#define glVertex3f            glesVertex3f
-#define glVertex3fv           glesVertex3fv
-#define glLightModeli         glesLightModeli
-
-#define APIENTRY
-//#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
-
-static EGLDisplay eglDisplay;
-static EGLSurface eglSurface;
-static EGLContext eglContext;
-static int eglWidth, eglHeight;
-
-static bool egl_init_display(ANativeWindow* window)
-{
-   const EGLint attribs[] =
-   {
-      EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-      EGL_BLUE_SIZE, 8,
-      EGL_GREEN_SIZE, 8,
-      EGL_RED_SIZE, 8,
-      EGL_DEPTH_SIZE, 24,
-      /*EGL_SAMPLE_BUFFERS, 1,
-      EGL_SAMPLES, 0, //2,*/
-      EGL_NONE
-   };
-   EGLint w, h, format;
-   EGLint numConfigs;
-   EGLConfig config;
-   EGLSurface surface;
-   EGLContext context;
-
-   EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-   eglInitialize(display, 0, 0);
-   eglChooseConfig(display, attribs, &config, 1, &numConfigs);
-   eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
-
-   surface = eglCreateWindowSurface(display, config, window, null);
-   context = eglCreateContext(display, config, null, null);
-
-   if(!eglMakeCurrent(display, surface, surface, context))
-      return false;
-
-   eglQuerySurface(display, surface, EGL_WIDTH, &w);
-   eglQuerySurface(display, surface, EGL_HEIGHT, &h);
-
-   eglDisplay = display;
-   eglContext = context;
-   eglSurface = surface;
-   eglWidth = w;
-   eglHeight = h;
-
-   glEnableClientState(GL_VERTEX_ARRAY);
-   /*
-   // 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();
-
-   glesMatrixMode(GL_MODELVIEW);
-   glScaled(1.0, 1.0, -1.0);
-   glesMatrixMode(GL_PROJECTION);
-   glShadeModel(GL_FLAT);
-
-   glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
-   glFogi(GL_FOG_MODE, GL_EXP);
-   glFogf(GL_FOG_DENSITY, 0);
-   glEnable(GL_NORMALIZE);
-   glDepthFunc(GL_LESS);
-   glClearDepth(1.0);
-   glDisable(GL_MULTISAMPLE_ARB);
-
-   glViewport(0,0,w,h);
-   glesLoadIdentity();
-   glOrtho(0,w,h,0,0.0,1.0);
-
-   currentVertexBuffer = 0;
-   return true;
-}
-
-static void egl_term_display()
-{
-   if(stippleTexture)
-   {
-      glDeleteTextures(1, &stippleTexture);
-      stippleTexture = 0;
-   }
-   if(eglDisplay != EGL_NO_DISPLAY)
-   {
-      eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-      if(eglContext != EGL_NO_CONTEXT)
-         eglDestroyContext(eglDisplay, eglContext);
-      if(eglSurface != EGL_NO_SURFACE)
-         eglDestroySurface(eglDisplay, eglSurface);
-      eglTerminate(eglDisplay);
-   }
-   eglDisplay = EGL_NO_DISPLAY;
-   eglContext = EGL_NO_CONTEXT;
-   eglSurface = EGL_NO_SURFACE;
-}
-
-// OpenGL Immediate Mode Porting Kit
-static int beginCount;
-static int vertexCount;
-static int normalCount;
-static float *vertexPointer;
-static float *normalPointer;
-static GLenum beginMode;
-static unsigned int beginBufferSize, normalBufferSize;
-static int numVertexCoords = 2;
-
-void glesRecti(int a, int b, int c, int d)
-{
-   glBegin(GL_QUADS);
-   glVertex2i(a, b);
-   glVertex2i(a, d);
-   glVertex2i(c, d);
-   glVertex2i(c, b);
-   glEnd();
-}
-
-void glesBegin(GLenum mode)
-{
-   beginMode = mode;
-   beginCount = 0;
-   vertexCount = 0;
-   if(!vertexPointer)
-   {
-      normalBufferSize = beginBufferSize = 1024;  // default number of vertices
-      vertexPointer = new float[beginBufferSize * 5];
-      normalPointer = new float[normalBufferSize * 3];
-   }
-}
-
-void glesTexCoord2f(float x, float y)
-{
-   int count = vertexCount;
-
-   if(vertexCount + numVertexCoords > beginBufferSize)
-   {
-      beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * 5];
-   }
-
-   vertexPointer[count*(2+numVertexCoords)  ] = x;
-   vertexPointer[count*(2+numVertexCoords)+1] = y;
-   count++;
-
-   if(beginMode == GL_QUADS && ((beginCount % 4) == 3))
-   {
-      vertexPointer[count*(2+numVertexCoords)  ] = vertexPointer[(count-4)*(2+numVertexCoords)];
-      vertexPointer[count*(2+numVertexCoords)+1] = vertexPointer[(count-4)*(2+numVertexCoords)+1];
-      count++;
-      vertexPointer[count*(2+numVertexCoords)  ] = vertexPointer[(count-3)*(2+numVertexCoords)];
-      vertexPointer[count*(2+numVertexCoords)+1] = vertexPointer[(count-3)*(2+numVertexCoords)+1];
-      count++;
-   }
-}
-void glesTexCoord2i(int x, int y)       { glesTexCoord2f((float)x, (float)y); }
-void glesTexCoord2d(double x, double y) { glesTexCoord2f((float)x, (float)y); }
-void glesTexCoord2fv(float * a)         { glesTexCoord2f(a[0], a[1]); }
-
-void glesVertex2f(float x, float y)
-{
-   numVertexCoords = 2;
-   if(vertexCount + 4 > beginBufferSize)
-   {
-      beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * 5];
-   }
-
-   vertexPointer[vertexCount*4+2] = x;
-   vertexPointer[vertexCount*4+3] = y;
-   vertexCount++;
-
-   if(beginMode == GL_QUADS && ((beginCount % 4) == 3))
-   {
-      vertexPointer[vertexCount*4+2] = vertexPointer[(vertexCount-4)*4+2];
-      vertexPointer[vertexCount*4+3] = vertexPointer[(vertexCount-4)*4+3];
-      vertexCount++;
-      vertexPointer[vertexCount*4+2] = vertexPointer[(vertexCount-3)*4+2];
-      vertexPointer[vertexCount*4+3] = vertexPointer[(vertexCount-3)*4+3];
-      vertexCount++;
-   }
-   beginCount++;
-}
-void glesVertex2i(int x, int y)         { glesVertex2f((float)x, (float)y); }
-void glesVertex2d(double x, double y)   { glesVertex2f((float)x, (float)y); }
-
-void glesEnd(void)
-{
-   int mode = beginMode;
-   if(mode == GL_QUADS)        mode = GL_TRIANGLES;
-   else if(mode == GL_POLYGON) mode = GL_TRIANGLE_FAN;
-   GLSelectVBO(0);
-   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-   glTexCoordPointer(numVertexCoords, GL_FLOAT, (numVertexCoords+2)*sizeof(float),vertexPointer);
-   glVertexPointer  (numVertexCoords, GL_FLOAT, (numVertexCoords+2)*sizeof(float),vertexPointer+2);
-   if(normalCount && normalCount == vertexCount)
-   {
-      glEnableClientState(GL_NORMAL_ARRAY);
-      glNormalPointer  (GL_FLOAT, 3*sizeof(float),normalPointer);
-   }
-
-   glDrawArrays(mode, 0, vertexCount);
-   if(normalCount)
-      glDisableClientState(GL_NORMAL_ARRAY);
-   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-   normalCount = 0;
-}
-
-// Vertex Pointer
-static float *floatVPBuffer = null;
-static short *shortVPBuffer = null;
-static unsigned int shortVPSize = 0, floatVPSize = 0;
-
-// Buffer Data
-//static float *floatVPBuffer = null;  // For floats we reuse floatVPBuffer
-static unsigned short *shortBDBuffer = null;
-static unsigned int shortBDSize = 0/*, floatVPSize = 0*/;
-
-void glesVertexPointeri(int numCoords, int stride, int *pointer, int numVertices)
-{
-   if(pointer)
-   {
-      int i;
-      if(numVertices*numCoords > shortVPSize)
-      {
-         shortVPSize = numVertices*numCoords;
-         shortVPBuffer = renew shortVPBuffer short[shortVPSize];
-      }
-      for(i = 0; i < numVertices*numCoords; i++)
-         shortVPBuffer[i] = (short)pointer[i];
-      glVertexPointer(numCoords, GL_SHORT, stride, shortVPBuffer);
-   }
-   else
-      glVertexPointer(numCoords, GL_SHORT, stride, 0);
-}
-
-void glesVertexPointerd(int numCoords, int stride, double *pointer, int numVertices)
-{
-   if(pointer)
-   {
-      int i;
-      if(numVertices*numCoords > floatVPSize)
-      {
-         floatVPSize = numVertices*numCoords;
-         floatVPBuffer = renew floatVPBuffer float[floatVPSize];
-      }
-      for(i = 0; i < numVertices*numCoords; i++)
-         floatVPBuffer[i] = (float)pointer[i];
-      glVertexPointer(numCoords, GL_FLOAT, stride, floatVPBuffer);
-   }
-   else
-      glVertexPointer(numCoords, GL_FLOAT, stride, 0);
-}
-
-void glesTexReuseIntVP(int numCoords)
-{
-   glTexCoordPointer(numCoords, GL_SHORT, 0, floatVPBuffer);
-}
-
-void glesTexReuseDoubleVP(int numCoords)
-{
-   glTexCoordPointer(numCoords, GL_FLOAT, 0, floatVPBuffer);
-}
-
-void glesColor3f( float r, float g, float b )
-{
-   glColor4f(r, g, b, 1.0f);
-}
-
-void glesColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-{
-   glColor4f(r/255.0f, g/255.0f, b/255.0f, a/255.0f);
-}
-
-void glesColor4fv(float * a)
-{
-   glColor4f(a[0], a[1], a[2], a[3]);
-}
-
-void glesBufferDatad(int target, int size, void * data, int usage)
-{
-   int numElems = size/sizeof(double);
-   double * dblPtr = (double *)data;
-   int i;
-   if (numElems > floatVPSize)
-   {
-      floatVPSize = numElems;
-      floatVPBuffer = renew floatVPBuffer float[floatVPSize];
-   }
-   for (i=0; i< numElems; i++)
-      floatVPBuffer[i] = (float)dblPtr[i];
-
-   glBufferData(target, numElems*sizeof(float), floatVPBuffer, usage);
-}
-
-void glesBufferDatai(int target, int size, void * data, int usage)
-{
-   int numElems = size/sizeof(unsigned int);
-   unsigned int * pointer = (unsigned int *)data;
-   int i;
-   if (numElems > shortBDSize)
-   {
-      shortBDSize = numElems;
-      shortBDBuffer = renew shortBDBuffer uint16[shortBDSize];
-   }
-   for (i=0; i< numElems; i++)
-      shortBDBuffer[i] = (unsigned short)pointer[i];
-
-   glBufferData(target, numElems*sizeof(unsigned short), shortBDBuffer, usage);
-}
-
-// *** Our Custom Matrix Stack ***
-
-static void LoadCurMatrix()
-{
-   double * i = matrixStack[curStack][matrixIndex[curStack]].array;
-   float m[16] =
-   {
-      (float)i[0],(float)i[1],(float)i[2],(float)i[3],
-      (float)i[4],(float)i[5],(float)i[6],(float)i[7],
-      (float)i[8],(float)i[9],(float)i[10],(float)i[11],
-      (float)i[12],(float)i[13],(float)i[14],(float)i[15]
-   };
-   glLoadMatrixf(m);
-}
-
-void glesLoadIdentity()
-{
-   matrixStack[curStack][matrixIndex[curStack]].Identity();
-   LoadCurMatrix();
-}
-
-void glesPushMatrix()
-{
-   if(matrixIndex[curStack] + 1 < sizeof(matrixStack[0]) / sizeof(Matrix))
-   {
-      matrixIndex[curStack]++;
-      memcpy(matrixStack[curStack][matrixIndex[curStack]].array, matrixStack[curStack][matrixIndex[curStack]-1].array, sizeof(Matrix));
-   }
-}
-
-void glesPopMatrix()
-{
-   if(matrixIndex[curStack] > 0)
-   {
-      matrixIndex[curStack]--;
-      LoadCurMatrix();
-   }
-}
-
-void glesLoadMatrixd(double * i)
-{
-   memcpy(matrixStack[curStack][matrixIndex[curStack]].array, i, sizeof(Matrix));
-   LoadCurMatrix();
-}
-
-void glesOrtho( double l, double r, double b, double t, double n, double f )
-{
-   Matrix m
-   { {
-      (2 / (r - l)), 0, 0, 0,
-      0, (2 / (t - b)), 0, 0,
-      0, 0, (-2 / (f - n)), 0,
-      (-(r + l) / (r - l)), (-(t + b) / (t - b)), (-(f + n) / (f - n)), 1
-   } };
-   Matrix res;
-   res.Multiply(m, matrixStack[curStack][matrixIndex[curStack]]);
-   matrixStack[curStack][matrixIndex[curStack]] = res;
-   LoadCurMatrix();
-}
-
-void glesFrustum( double l, double r, double b, double t, double n, double f )
-{
-   nearPlane = n;
-   n = 1;
-   l /= nearPlane;
-   r /= nearPlane;
-   b /= nearPlane;
-   t /= nearPlane;
-   f /= nearPlane;
-   {
-      double A = ((r + l) / (r - l));
-      double B = ((t + b) / (t - b));
-      double C = (-(f + n) / (f - n));
-      double D = (-2*f*n/(f-n));
-      Matrix m
-      { {
-         (2.0*n / (r - l)), 0, 0, 0,
-         0, (2.0*n / (t - b)), 0, 0,
-         A, B,             C,-1,
-         0, 0,             D, 0
-      } };
-      Matrix res;
-      res.Multiply(m, matrixStack[curStack][matrixIndex[curStack]]);
-      matrixStack[curStack][matrixIndex[curStack]] = res;
-      LoadCurMatrix();
-   }
-}
-
-void glesRotated( double a, double b, double c, double d )
-{
-   Matrix m;
-   Quaternion q;
-   q.RotationAxis({(float)b,(float)-c,(float)d}, a );
-   m.RotationQuaternion(q);
-   matrixStack[curStack][matrixIndex[curStack]].Rotate(q);
-   LoadCurMatrix();
-}
-void glesScaled( double a, double b, double c )
-{
-   Matrix m, r;
-
-   m.Identity();
-   m.Scale(a,b,c);
-   r.Multiply(m, matrixStack[curStack][matrixIndex[curStack]]);
-   matrixStack[curStack][matrixIndex[curStack]] = r;
-   LoadCurMatrix();
-}
-
-void glesTranslated( double a, double b, double c )
-{
-   Matrix m, r;
-
-   m.Identity();
-   m.Translate(a,b,c);
-   r.Multiply(m, matrixStack[curStack][matrixIndex[curStack]]);
-   matrixStack[curStack][matrixIndex[curStack]] = r;
-   LoadCurMatrix();
-}
-
-void glesMultMatrixd( double * i )
-{
-   Matrix r;
-   r.Multiply((Matrix *)i, matrixStack[curStack][matrixIndex[curStack]]);
-   matrixStack[curStack][matrixIndex[curStack]] = r;
-   LoadCurMatrix();
-}
-
-void glesMatrixMode(int mode)
-{
-   curStack = mode == GL_MODELVIEW ? 0 : mode == GL_PROJECTION ? 1 : 2;
-   glMatrixMode(mode);
-}
 
-#define glPushMatrix          glesPushMatrix
-#define glPopMatrix           glesPopMatrix
-#define glLoadIdentity        glesLoadIdentity
-#define glMatrixMode          glesMatrixMode
+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:
 
-/* Using the built-in matrix stack
-void glesLoadMatrixd( double * i )
-{
-   float m[16] =
-   {
-      (float)i[0],(float)i[1],(float)i[2],(float)i[3],
-      (float)i[4],(float)i[5],(float)i[6],(float)i[7],
-      (float)i[8],(float)i[9],(float)i[10],(float)i[11],
-      (float)i[12],(float)i[13],(float)i[14],(float)i[15]
-   };
-   glLoadMatrixf(m);
-}
 
-void glesOrtho( double l, double r, double b, double t, double n, double f )
-{
-   float matrix[4][4] =
-   {
-      { (float)(2 / (r - l)), 0, 0, 0 },
-      { 0, (float)(2 / (t - b)), 0, 0 },
-      { 0, 0, (float)(-2 / (f - n)), 0 },
-      { (float)(-(r + l) / (r - l)), (float)(-(t + b) / (t - b)), (float)(-(f + n) / (f - n)), 1 }
-   };
-   glMultMatrixf((float *)matrix);
-}
-
-void glesFrustum( double l, double r, double b, double t, double n, double f )
-{
-   float A = (float)((r + l) / (r - l));
-   float B = (float)((t + b) / (t - b));
-   float C = (float)(-(f + n) / (f - n));
-   float D = (float)(-2*f*n/(f-n));
-   float matrix[4][4] =
-   {
-      { (float)(2*n / (r - l)), 0, 0, 0 },
-      { 0, (float)(2*n / (t - b)), 0, 0 },
-      { A, B,             C,-1 },
-      { 0, 0,             D, 0 }
-   };
-   glMultMatrixf((float *)matrix);
-}
-
-void glesRotated( double a, double b, double c, double d ) { glRotatef((float)a, (float)b, (float)c, (float)d); }
-void glesScaled( double a, double b, double c ) { glScalef((float)a, (float)b, (float)c); }
-void glesTranslated( double a, double b, double c ) { glTranslatef((float)a, (float)b, (float)c); }
-
-void glesMultMatrixd( double * i )
+// **********   Errors and Debugging   **********
+/*
+void CheckGLErrors()
 {
-   float m[16] =
-   {
-      (float)i[0], (float)i[1], (float)i[2], (float)i[3],
-      (float)i[4], (float)i[5], (float)i[6], (float)i[7],
-      (float)i[8], (float)i[9], (float)i[10], (float)i[11],
-      (float)i[12], (float)i[13], (float)i[14], (float)i[15]
-   };
-   glMultMatrixf(m);
+   int e, nCount = 0;
+   while((e = glGetError()) && nCount++ < 10)
+      printf("GL error %d!\n", e);
 }
 */
-
-// Need to do these...
-void glesVertex3f( float x, float y, float z )
+#ifdef GL_DEBUGGING
+#ifndef APIENTRY
+   #define APIENTRY
+#endif
+static void APIENTRY openglCallbackFunction(GLenum source,
+                                           GLenum type,
+                                           GLuint id,
+                                           GLenum severity,
+                                           GLsizei length,
+                                           const GLchar* message,
+                                           const void* userParam)
 {
-   numVertexCoords = 3;
-   if(vertexCount + 4 > beginBufferSize)
+   if(severity == GL_DEBUG_SEVERITY_NOTIFICATION)
+      return;
+   PrintLn("---------------------opengl-callback-start------------");
+   PrintLn("message: ", message);
+   PrintLn("type: ");
+   switch (type)
    {
-      beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * 5];
+      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;
    }
 
-   vertexPointer[vertexCount*5+2] = x;
-   vertexPointer[vertexCount*5+3] = y;
-   vertexPointer[vertexCount*5+4] = z;
-   vertexCount++;
-
-   if(beginMode == GL_QUADS && ((beginCount % 4) == 3))
+   PrintLn("id: ", id);
+   Print("severity: ");
+   switch (severity)
    {
-      vertexPointer[vertexCount*5+2] = vertexPointer[(vertexCount-4)*5+2];
-      vertexPointer[vertexCount*5+3] = vertexPointer[(vertexCount-4)*5+3];
-      vertexPointer[vertexCount*5+4] = vertexPointer[(vertexCount-4)*5+4];
-      vertexCount++;
-      vertexPointer[vertexCount*5+2] = vertexPointer[(vertexCount-3)*5+2];
-      vertexPointer[vertexCount*5+3] = vertexPointer[(vertexCount-3)*5+3];
-      vertexPointer[vertexCount*5+4] = vertexPointer[(vertexCount-3)*5+4];
-      vertexCount++;
+      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)");
    }
-   beginCount++;
+   PrintLn("---------------------opengl-callback-end--------------");
 }
 
-void glesVertex3d( double x, double y, double z )  { glesVertex3f((float)x, (float)y, (float)z); }
-void glesVertex3fv( float* coords )                { glesVertex3f(coords[0], coords[1], coords[2]); }
-
-void glesNormal3f(float x, float y, float z)
+static void setupDebugging()
 {
-   normalCount = vertexCount;
-   if(vertexCount + 4 > normalBufferSize)
+   if(glDebugMessageCallback)
    {
-      normalBufferSize = normalBufferSize + normalBufferSize/2;
-      normalPointer = renew normalPointer float[normalBufferSize * 2];
-   }
+      GLuint unusedIds = 0;
 
-   normalPointer[normalCount*3+0] = x;
-   normalPointer[normalCount*3+1] = y;
-   normalPointer[normalCount*3+2] = z;
-   normalCount++;
+      glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
 
-   if(beginMode == GL_QUADS && ((beginCount % 4) == 3))
-   {
-      normalPointer[normalCount*3+0] = normalPointer[(normalCount-4)*3+0];
-      normalPointer[normalCount*3+1] = normalPointer[(normalCount-4)*3+1];
-      normalPointer[normalCount*3+2] = normalPointer[(normalCount-4)*3+2];
-      normalCount++;
-      normalPointer[normalCount*3+0] = normalPointer[(normalCount-3)*3+0];
-      normalPointer[normalCount*3+1] = normalPointer[(normalCount-3)*3+1];
-      normalPointer[normalCount*3+2] = normalPointer[(normalCount-3)*3+2];
-      normalCount++;
+      glDebugMessageCallback(openglCallbackFunction, null);
+      glDebugMessageControl(GL_DONT_CARE,
+          GL_DONT_CARE,
+          GL_DONT_CARE,
+          0,
+          &unusedIds,
+          GL_TRUE);
    }
 }
-void glesNormal3fd(double x, double y, double z)         { glesNormal3f((float)x, (float)y, (float)z); }
-void glesNormal3fv(float * coords)                       { glesNormal3f(coords[0], coords[1], coords[2]); }
-
-void glesColorMaterial(int a, int b)
-{
-   PrintLn("glColorMaterial stub");
-}
-
-void glesTerminate()
-{
-   delete vertexPointer;
-   beginBufferSize = 0;
-
-   delete floatVPBuffer;
-   shortVPSize = 0;
-
-   delete shortVPBuffer;
-   floatVPSize = 0;
+#endif
 
-   delete shortBDBuffer;
-   shortBDSize = 0;
-}
 
 static GLuint stippleTexture;
 static bool stippleEnabled;
 
-void glesLineStipple( int i, unsigned short j )
+public void glsupLineStipple( int i, uint16 j )
 {
    uint texture[1*16];
    int x;
@@ -1057,151 +255,132 @@ void glesLineStipple( int i, unsigned short j )
       glGenTextures(1, &stippleTexture);
    glBindTexture(GL_TEXTURE_2D, stippleTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
-   glEnable(GL_TEXTURE_2D);
+
+   // TOOD: Special shading code for stippling?
+   GLSetupTexturing(true);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    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(GL_PROJECTION);
+   GLScaled(i/16.0, 1, 1.0f);
+   GLTranslated(0.5, 0.5, 0);
+   GLMatrixMode(MatrixMode::projection);
 }
 
-void glesLightModeli( unsigned int pname, int param )
-{
-   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
+   }
+
+#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)
-{
-   glBlendFunc(a, b);
-}
-
-// 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) { }
-
-#else
-
-/* Non OpenGL ES friendly stuff
-#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
-*/
-
-#endif
-
-#if !defined(__APPLE__)
-void (APIENTRY * glBindBufferARB) (GLenum target, GLuint buffer);
-void (APIENTRY * glGenBuffersARB) (GLsizei n, GLuint *buffers);
-void (APIENTRY * glDeleteBuffersARB) (GLsizei n, const GLuint *buffers);
-void (APIENTRY * glBufferDataARB) (GLenum target, int size, const GLvoid *data, GLenum usage);
-#endif
-
 #endif
 
-static int currentVertexBuffer;
-
-bool GLSelectVBO(uint vbo)
+#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
+static inline uint getPrimitiveType(RenderPrimitiveType type)
 {
-   if(currentVertexBuffer != vbo)
+   static int primitiveTypes[RenderPrimitiveType] =
    {
-      GLBindBuffer(GL_ARRAY_BUFFER, vbo);
-      currentVertexBuffer = vbo;
-      return true;
-   }
-   return false;
+      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];
 }
 
-void GLGenBuffers(int count, uint * buffer)
+public void GLSetupTexturing(bool enable)
 {
-#ifdef __ANDROID__
-   glGenBuffers(count, buffer);
-#else
-#if defined(__WIN32__)
-   if(glGenBuffersARB)
-#endif
-      glGenBuffersARB(count, buffer);
+#if ENABLE_GL_SHADERS
+   if(glCaps_shaders)
+      defaultShader.texturing(enable);
 #endif
-}
 
-void GLDeleteBuffers(int count, GLuint * buffer)
-{
-#ifdef __ANDROID__
-   glDeleteBuffers(count, buffer);
-#else
-#if defined(__WIN32__)
-   if(glDeleteBuffersARB)
-#endif
-      glDeleteBuffersARB(count, buffer);
+#if ENABLE_GL_FFP
+   if(!glCaps_shaders)
+      (enable ? glEnable : glDisable)(GL_TEXTURE_2D);
 #endif
 }
 
-void GLBindBuffer(int target, uint buffer)
+public void GLSetupFog(bool enable)
 {
-#ifdef __ANDROID__
-   glBindBuffer(target, buffer);
-#else
-#if defined(__WIN32__)
-   if(glBindBufferARB)
+#if ENABLE_GL_SHADERS
+   if(glCaps_shaders)
+      defaultShader.fog(enable);
 #endif
-      glBindBufferARB(target, buffer);
+
+#if ENABLE_GL_FFP
+   if(!glCaps_shaders)
+      (enable ? glEnable : glDisable)(GL_FOG);
 #endif
 }
 
-void GLBufferData(int type, GLenum target, int size, const GLvoid *data, GLenum usage)
-{
-#ifdef __ANDROID__
-   if(type == GL_DOUBLE)
-      glesBufferDatad(target, size, (void *)data, usage);
-   else if(type == GL_UNSIGNED_INT)
-      glesBufferDatai(target, size, (void *)data, usage);
-   else
-      glBufferData(target, size, data, usage);
-#else
+bool lightingEnabled;
 
-#if defined(__WIN32__)
-   if(glBufferDataARB)
+public void GLSetupLighting(bool enable)
+{
+   lightingEnabled = enable;
+#if ENABLE_GL_SHADERS
+   if(glCaps_shaders)
+      defaultShader.lighting(enable);
 #endif
-      glBufferDataARB(target, size, data, usage);
 
+#if ENABLE_GL_FFP
+   if(!glCaps_shaders)
+      (enable ? glEnable : glDisable)(GL_LIGHTING);
 #endif
 }
+#endif
+
+/*static */GLuint lastBlitTex;
+
+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;
@@ -1215,7 +394,7 @@ class OGLDisplay : struct
    int imageBuffers[2];
    byte * pboMemory1, * pboMemory2;
    */
-#elif !defined(__ANDROID__)
+#elif !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
    GLXContext glContext;
 
    Pixmap pixmap;
@@ -1231,29 +410,32 @@ class OGLDisplay : struct
    Pixmap shapePixmap;
    X11Picture shapePicture;
 #endif
-
-   ColorAlpha * flippingBuffer;
-   int flipBufH, flipBufW;
-   bool depthWrite;
-   int x, y;
 };
 
 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__)
+#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
@@ -1262,43 +444,98 @@ class OGLSurface : struct
    bool opaqueText;
    int xOffset;
    bool writingText;
+   bool writingOutline;
 
    float foreground[4], background[4], bitmapMult[4];
 } OGLSurface;
 
 class OGLMesh : struct
 {
-   uint vertices;
-   uint normals;
-   uint texCoords;
-   uint texCoords2;
-   uint colors;
+   GLAB vertices;
+   GLAB normals;
+   GLAB tangents;
+   GLAB lightVectors;
+   GLAB texCoords;
+   GLAB texCoords2;
+   GLAB colors;
 };
 
 class OGLIndices : struct
 {
    uint16 * indices;
-   uint buffer;
+   GLEAB buffer;
    uint nIndices;
 };
 
-#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
-static int primitiveTypes[RenderPrimitiveType] =
-{
-   GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_LINE_STRIP
-};
-#endif
-
 int current;
 void * previous;
 
+#if defined(__WIN32__)
+static HGLRC winCreateContext(HDC hdc, int * contextVersion, bool * isCompatible, bool compatible)
+{
+   HGLRC result = 0;
+   if(wglCreateContextAttribsARB)
+   {
+      int versions[12][2] =
+      {
+         { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 },
+                             { 3, 3 }, { 3, 2 }, { 3, 1 }, { 3, 0 },
+                                                 { 2, 1 }, { 2, 0 }
+      };
+
+      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;
+      }
+   }
+   if(!result)
+   {
+      if(contextVersion) *contextVersion = 1;
+      if(isCompatible)   *isCompatible = true;
+      result = wglCreateContext(hdc);
+   }
+   return result;
+}
+#endif
+
 class OpenGLDisplayDriver : DisplayDriver
 {
    class_property(name) = "OpenGL";
 
    bool LockSystem(DisplaySystem displaySystem)
    {
-#if !defined(__ANDROID__)
+#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__)
@@ -1310,6 +547,8 @@ class OpenGLDisplayDriver : DisplayDriver
       //previous = oglSystem.glContext;
    #endif
 #endif
+      GLABBindBuffer(GL_ARRAY_BUFFER, 0);
+      GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
       return true;
    }
 
@@ -1320,7 +559,7 @@ class OpenGLDisplayDriver : DisplayDriver
       wglMakeCurrent(null, null);
    #elif defined(__unix__) || defined(__APPLE__)
       // printf("Making NULL current\n");
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
       #else
       glXMakeCurrent(xGlobalDisplay, None, null);
       #endif
@@ -1330,7 +569,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
    bool Lock(Display display)
    {
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
       OGLDisplay oglDisplay = display.driverData;
       if(useSingleGLContext) return true;
    #if defined(__WIN32__)
@@ -1341,6 +580,8 @@ class OpenGLDisplayDriver : DisplayDriver
       glXMakeCurrent(xGlobalDisplay, (GLXDrawable)display.window, oglDisplay.glContext);
    #endif
 #endif
+      GLABBindBuffer(GL_ARRAY_BUFFER, 0);
+      GLABBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
       return true;
    }
 
@@ -1378,7 +619,7 @@ class OpenGLDisplayDriver : DisplayDriver
          if(oglDisplay.memBitmap) DeleteObject(oglDisplay.memBitmap);
 
    #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
       #else
          if(oglDisplay.shapePixmap)
             XFreePixmap(xGlobalDisplay, oglDisplay.shapePixmap);
@@ -1419,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);
 
@@ -1459,20 +764,6 @@ class OpenGLDisplayDriver : DisplayDriver
             {
                wglMakeCurrent(oglSystem.hdc, oglSystem.glrc);
 
-                // Get Pointers To The GL Functions
-               glActiveTextureARB = (void *) wglGetProcAddress("glActiveTextureARB");
-               glMultiTexCoord2fARB = (void *) wglGetProcAddress("glMultiTexCoord2fARB");
-               glClientActiveTextureARB = (void *) wglGetProcAddress("glClientActiveTextureARB");
-               glLockArraysEXT = (void *) wglGetProcAddress("glLockArraysEXT" );
-               glUnlockArraysEXT = (void *) wglGetProcAddress("glUnlockArraysEXT");
-                glGenBuffersARB = (void *) wglGetProcAddress("glGenBuffersARB");
-                glBindBufferARB = (void *) wglGetProcAddress("glBindBufferARB");
-                glBufferDataARB = (void *) wglGetProcAddress("glBufferDataARB");
-               glMapBufferARB  = (void *) wglGetProcAddress("glMapBufferARB");
-               glUnmapBufferARB  = (void *) wglGetProcAddress("glUnmapBufferARB");
-                glDeleteBuffersARB = (void *) wglGetProcAddress("glDeleteBuffersARB");
-               glBlendFuncSeparate = (void *) wglGetProcAddress("glBlendFuncSeparate");
-
                wglChoosePixelFormatARB = (void *) wglGetProcAddress("wglChoosePixelFormatARB");
                wglGetExtensionsStringARB = (void *)wglGetProcAddress("wglGetExtensionsStringARB");
                wglCreatePbufferARB = (void *)wglGetProcAddress("wglCreatePbufferARB");
@@ -1482,42 +773,43 @@ class OpenGLDisplayDriver : DisplayDriver
                wglReleasePbufferDCARB = (void *)wglGetProcAddress("wglReleasePbufferDCARB");
                wglBindTexImageARB = (void *)wglGetProcAddress("wglBindTexImageARB");
                wglReleaseTexImageARB = (void *)wglGetProcAddress("wglReleaseTexImageARB");
-
                wglSwapIntervalEXT = (void *)wglGetProcAddress("wglSwapIntervalEXT");
+               wglCreateContextAttribsARB = (void *)wglGetProcAddress("wglCreateContextAttribsARB");
 
-               vboAvailable = glBindBufferARB != null;
+               glLockArraysEXT = (void *) wglGetProcAddress("glLockArraysEXT" );
+               glUnlockArraysEXT = (void *) wglGetProcAddress("glUnlockArraysEXT");
 
                // eSystem_LoggingMode(LOG_MSGBOX, null);
 
                if(wglChoosePixelFormatARB)
                {
-                   int pixelFormat;
-                   int valid;
-                   int numFormats;
-                   float fAttributes[] = {0,0};
-                   int iAttributes[] =
+                  int pixelFormat;
+                  int valid;
+                  int numFormats;
+                  float fAttributes[] = {0,0};
+                  int iAttributes[] =
                   {
                      WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
-                           WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
-                           WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
-                           WGL_COLOR_BITS_ARB,24,
-                           WGL_ALPHA_BITS_ARB,8,
-                           WGL_DEPTH_BITS_ARB,16,
-                           WGL_STENCIL_BITS_ARB,0,
-                           WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
-                           WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
-                           WGL_SAMPLES_ARB, 4,                                         // Check For 4x Multisampling
-                           0,0
+                     WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
+                     WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
+                     WGL_COLOR_BITS_ARB,24,
+                     WGL_ALPHA_BITS_ARB,8,
+                     WGL_DEPTH_BITS_ARB,16,
+                     WGL_STENCIL_BITS_ARB,0,
+                     WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
+                     WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
+                     WGL_SAMPLES_ARB, 4,                  // Check For 4x Multisampling
+                     0,0
                   };
 
                   //Log("Found wglChoosePixelFormatARB\n");
 
-                   valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
-                   if(!valid || !numFormats)
-                   {
+                  valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
+                  if(!valid || !numFormats)
+                  {
                      //Log("Can't find 4x multi sampling\n");
-                      iAttributes[19] = 2;
-                      valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
+                     iAttributes[19] = 2;
+                     valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
                      if(!valid || !numFormats)
                      {
                         // Log("Can't find 2x multi sampling\n");
@@ -1525,41 +817,121 @@ class OpenGLDisplayDriver : DisplayDriver
                         iAttributes[17] = 0;
                         valid = wglChoosePixelFormatARB(oglSystem.hdc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
                      }
-                   }
+                  }
                   if(valid && numFormats)
                   {
                      oglSystem.format = pixelFormat;
                      wglMakeCurrent(null, null);
                      wglDeleteContext(oglSystem.glrc);
 
-                     // *** DescribePixelFormat does not support WGL pixel formats! ***
-                     //DescribePixelFormat(oglSystem.hdc, oglSystem.format, sizeof(oglSystem.pfd), &oglSystem.pfd);
-                     SetPixelFormat(oglSystem.hdc, oglSystem.format, &oglSystem.pfd);
-                     //Log("Successfully set pixel format\n");
+                     // *** DescribePixelFormat does not support WGL pixel formats! ***
+                     //DescribePixelFormat(oglSystem.hdc, oglSystem.format, sizeof(oglSystem.pfd), &oglSystem.pfd);
+                     SetPixelFormat(oglSystem.hdc, oglSystem.format, &oglSystem.pfd);
+                     //Log("Successfully set pixel format\n");
+
+#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
+                  eSystem_Logf("Can't find wglChoosePixelFormatARB\n");*/
+
+               result = true;
+
+               wglMakeCurrent(null, null);
+
+               //eSystem_DumpErrors(true);
+            }
+         }
+      }
+   #elif defined(__unix__) || defined(__APPLE__)
+      #if defined(__ANDROID__) || defined(__ODROID__)
+         #if defined(__ANDROID__)
+         egl_init_display(guiApp.desktop.windowHandle);
+         #elif defined(__ODROID__)
+         egl_init_display((uint)displaySystem.window);
+         #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);
 
-                     oglSystem.glrc = wglCreateContext(oglSystem.hdc);
-                     wglMakeCurrent(oglSystem.hdc, oglSystem.glrc);
-                  }
-               }
-               /*else
-                  eSystem_Logf("Can't find wglChoosePixelFormatARB\n");*/
+#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);
 
-               result = true;
+         glViewport(0,0,eglWidth,eglHeight);
+         GLLoadIdentity();
+         GLOrtho(0,eglWidth,eglHeight,0,0.0,1.0);
 
-               CheckExtensions(oglSystem);
+         glabCurArrayBuffer = 0;
+         glabCurElementBuffer = 0;
 
-               wglMakeCurrent(null, null);
+         result = true;
+      #elif defined(__EMSCRIPTEN__)
+         {
+            EmscriptenWebGLContextAttributes attribs = { 0 };
+            attribs.depth = 1;
+            attribs.antialias = 1;
 
-               //eSystem_DumpErrors(true);
-            }
+            /*
+              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;
+
+            /*glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+            glEnable(GL_BLEND);*/
          }
-      }
-   #elif defined(__unix__) || defined(__APPLE__)
-      vboAvailable = true;
-      #if defined(__ANDROID__)
-         egl_init_display(guiApp.desktop.windowHandle);
-         CheckExtensions(oglSystem);
-         result = true;
       #else
       {
          X11Window root = RootWindow( xGlobalDisplay, DefaultScreen( xGlobalDisplay ) );
@@ -1592,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;
          }
@@ -1610,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 );
@@ -1622,8 +1005,10 @@ class OpenGLDisplayDriver : DisplayDriver
       DestroyWindow(oglSystem.hwnd);
 
    #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__)
          egl_term_display();
+      #elif defined(__EMSCRIPTEN__)
+         emscripten_webgl_destroy_context(oglSystem.glc);
       #else
       if(oglSystem.visualInfo)
       {
@@ -1633,6 +1018,8 @@ class OpenGLDisplayDriver : DisplayDriver
          XFree(oglSystem.visualInfo);
    #endif
       }
+      if(oglSystem.glContext)
+        glXDestroyContext(xGlobalDisplay, oglSystem.glContext);
 
       if(oglSystem.glxDrawable)
       {
@@ -1644,25 +1031,189 @@ class OpenGLDisplayDriver : DisplayDriver
       delete oglSystem;
    }
 
+   /*static */bool ::initialDisplaySetup(Display display, bool canCheckExtensions, bool loadExtensions)
+   {
+      OGLDisplay oglDisplay = display.driverData;
+      OGLSystem oglSystem = display.displaySystem.driverData;
+      bool result = true;
+
+#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);
+
+#if defined(__WIN32__)
+      if(glBlendFuncSeparate)
+         glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+      else
+         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+#else
+      glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+#endif
+      glEnable(GL_BLEND);
+
+      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);
+      GLLoadIdentity();
+      if(display.width && display.height)
+         GLOrtho(0,display.width,display.height,0,0.0,1.0);
+
+#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);
+#if !defined(__EMSCRIPTEN__)
+      glDisable(GL_MULTISAMPLE);
+#endif
+
+#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
+      display.ambient = Color { 50,50,50 };
+#endif
+      return result;
+   }
+
    bool CreateDisplay(Display display)
    {
       bool result = false;
       OGLDisplay oglDisplay = display.driverData;
-#if !defined(__ANDROID__)
       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 = wglCreateContext(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);
@@ -1670,9 +1221,10 @@ class OpenGLDisplayDriver : DisplayDriver
          }
          else
             ReleaseDC(display.window, oglDisplay.hdc);
-   #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
-      #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__)
@@ -1691,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");
@@ -1706,72 +1263,56 @@ 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)
       {
-#if defined(__WIN32__)
-         if(glBlendFuncSeparate)
-            glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-         else
-            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#else
-#if !defined(__OLDX__)
-          glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-#else
-         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#endif
+#if defined(__EMSCRIPTEN__)
+         emscripten_webgl_make_context_current(oglSystem.glc);
 #endif
-         glEnable(GL_BLEND);
-
-         glMatrixMode(GL_MODELVIEW);
-         glScaled(1.0, 1.0, -1.0);
-         // glTranslatef(0.375f, 0.375f, 0.0f);
-         // glTranslatef(-0.625f, -0.625f, 0.0f);
-         glMatrixMode(GL_PROJECTION);
-         glShadeModel(GL_FLAT);
 
-         // 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);
-         glDepthFunc(GL_LESS);
-         glClearDepth(1.0);
-         glDisable(GL_MULTISAMPLE_ARB);
-      }
-#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
-      display.ambient = Color { 50,50,50 };
+#if defined(__WIN32__) || defined(USEPBUFFER)
+         initialDisplaySetup(display, !display.alphaBlend, true);
+#else
+         initialDisplaySetup(display, true, true);
 #endif
+      }
 
       if(!useSingleGLContext)
       {
    #if defined(__WIN32__)
          wglMakeCurrent(null, null);
    #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
          result = true;
       #else
          glXMakeCurrent(xGlobalDisplay, None, null);
       #endif
    #endif
       }
-
+      else
+      {
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
+         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)
@@ -1792,16 +1333,16 @@ class OpenGLDisplayDriver : DisplayDriver
             {
                //WGL_DRAW_TO_BITMAP_ARB, GL_TRUE,
                WGL_DRAW_TO_PBUFFER_ARB,GL_TRUE,
-                   WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
-                   WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
-                   WGL_COLOR_BITS_ARB,24,
-                   WGL_ALPHA_BITS_ARB,8,
-                   WGL_DEPTH_BITS_ARB,16,
-                   WGL_STENCIL_BITS_ARB,0,
-                   WGL_DOUBLE_BUFFER_ARB,GL_FALSE,
-                   WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
-                   WGL_SAMPLES_ARB, 4,                                         // Check For 4x Multisampling
-                   0,0
+               WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
+               WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
+               WGL_COLOR_BITS_ARB,24,
+               WGL_ALPHA_BITS_ARB,8,
+               WGL_DEPTH_BITS_ARB,16,
+               WGL_STENCIL_BITS_ARB,0,
+               WGL_DOUBLE_BUFFER_ARB,GL_FALSE,
+               WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
+               WGL_SAMPLES_ARB, 4,                  // Check For 4x Multisampling
+               0,0
             };
 
             //Log("Found wglChoosePixelFormatARB\n");
@@ -1855,7 +1396,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
          oglDisplay.pBuffer = wglCreatePbufferARB(oglSystem.hdc, pixelFormat, width, height, attributes);
          oglDisplay.hdc = wglGetPbufferDCARB(oglDisplay.pBuffer);
-         if((oglDisplay.glrc = wglCreateContext(oglDisplay.hdc)))
+         if((oglDisplay.glrc = winCreateContext(oglDisplay.hdc, null, null, oglDisplay.capabilities.compatible)))
          {
             BITMAPINFO * info;
             HDC hdc = GetDC(display.window);
@@ -1931,36 +1472,36 @@ class OpenGLDisplayDriver : DisplayDriver
             ReleaseDC(display.window, hdc);
          }
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
          result = true;
       #else
-       int attrib[] =
-       {
-               GLX_DOUBLEBUFFER,  True,
+         int attrib[] =
+         {
+            GLX_DOUBLEBUFFER,  True,
             GLX_DEPTH_SIZE,    1,
-               GLX_RED_SIZE,      8,
-               GLX_GREEN_SIZE,    8,
-               GLX_BLUE_SIZE,     8,
-               GLX_ALPHA_SIZE,    8,
-               GLX_STENCIL_SIZE,  1,
-               //GLX_DEPTH_SIZE,    24,
-               GLX_RENDER_TYPE,   GLX_RGBA_BIT,
-               GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT,
-               None
-       };
-
-       int PBattrib[] =
-       {
-               GLX_PBUFFER_WIDTH,   width,
-               GLX_PBUFFER_HEIGHT,  height,
-               GLX_LARGEST_PBUFFER, False,
+            GLX_RED_SIZE,      8,
+            GLX_GREEN_SIZE,    8,
+            GLX_BLUE_SIZE,     8,
+            GLX_ALPHA_SIZE,    8,
+            GLX_STENCIL_SIZE,  1,
+            //GLX_DEPTH_SIZE,    24,
+            GLX_RENDER_TYPE,   GLX_RGBA_BIT,
+            GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT | GLX_WINDOW_BIT,
+            None
+         };
+
+         int PBattrib[] =
+         {
+            GLX_PBUFFER_WIDTH,   width,
+            GLX_PBUFFER_HEIGHT,  height,
+            GLX_LARGEST_PBUFFER, False,
             None
-       };
+         };
 
-       // choose a pixel format that meets our minimum requirements
-       int count = 0;
+         // choose a pixel format that meets our minimum requirements
+         int count = 0;
 
-       GLXFBConfig *config = glXChooseFBConfig(xGlobalDisplay, DefaultScreen(xGlobalDisplay), attrib, &count);
+         GLXFBConfig *config = glXChooseFBConfig(xGlobalDisplay, DefaultScreen(xGlobalDisplay), attrib, &count);
          if(config)
          {
             if(oglDisplay.pixmap)
@@ -2009,14 +1550,14 @@ class OpenGLDisplayDriver : DisplayDriver
                XFreePixmap(xGlobalDisplay, oglDisplay.pixmap);
 
             if(oglDisplay.glContext)
-                 glXDestroyContext(xGlobalDisplay, oglDisplay.glContext);
+              glXDestroyContext(xGlobalDisplay, oglDisplay.glContext);
             if(oglDisplay.pBuffer)
                glXDestroyPbuffer(xGlobalDisplay, oglDisplay.pBuffer);
 
-               oglDisplay.pBuffer = glXCreatePbuffer(xGlobalDisplay, config[0], PBattrib);
+            oglDisplay.pBuffer = glXCreatePbuffer(xGlobalDisplay, config[0], PBattrib);
             if(oglDisplay.pBuffer)
             {
-                  oglDisplay.glContext = glXCreateNewContext(xGlobalDisplay, config[0], GLX_RGBA_TYPE, oglSystem.glContext, True);
+               oglDisplay.glContext = glXCreateNewContext(xGlobalDisplay, config[0], GLX_RGBA_TYPE, oglSystem.glContext, True);
                if(oglDisplay.glContext)
                {
                   glXMakeCurrent(xGlobalDisplay, None, null);
@@ -2062,7 +1603,7 @@ class OpenGLDisplayDriver : DisplayDriver
                                           {
                                              XRenderPictureAttributes attributes = { 0 };
                                              XRenderPictFormat * format = XRenderFindStandardFormat(xGlobalDisplay, /*PictStandardRGB24*/ PictStandardARGB32);
-                                             #if !defined(__APPLE__) && !defined(__OLDX__)
+                                             #if !defined(__APPLE__)
                                              attributes.repeat = RepeatNormal;
                                              #else
                                              attributes.repeat = 1;
@@ -2095,9 +1636,11 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__)
          wglMakeCurrent(oglDisplay.hdc, oglDisplay.glrc);
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #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
@@ -2106,6 +1649,12 @@ class OpenGLDisplayDriver : DisplayDriver
       else
 #endif
          result = true;
+
+      SETCAPS(oglDisplay.capabilities);
+
+      if(display.alphaBlend && result)
+         initialDisplaySetup(display, true, false);
+
       if(!result && display.alphaBlend)
       {
          printf("Alpha blending windows not supported on this display\n");
@@ -2116,8 +1665,9 @@ class OpenGLDisplayDriver : DisplayDriver
       result = false;
 
       glViewport(0,0,width,height);
-      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;
 
@@ -2125,7 +1675,11 @@ class OpenGLDisplayDriver : DisplayDriver
       {
          oglDisplay.flipBufW = width;
          oglDisplay.flipBufH = height;
+#if defined(_GLES) || defined(_GLES2)
+         result = true;
+#else
          oglDisplay.flippingBuffer = renew oglDisplay.flippingBuffer ColorAlpha [width * height];
+#endif
       }
       if(oglDisplay.flippingBuffer || !width || !height)
          result = true;
@@ -2151,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)
@@ -2166,10 +1732,12 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__) || defined(USEPBUFFER)
       OGLDisplay oglDisplay = display.driverData;
 #endif
-      //Logf("DisplayScreen\n");
 
-      glFlush();
-      glFinish();
+#if !defined(__ANDROID__)
+      /*glFlush();
+      glFinish();*/
+#endif
+
 #if defined(__WIN32__) || defined(USEPBUFFER)
       if(display.alphaBlend)
       {
@@ -2229,7 +1797,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
             ReleaseDC(0, hdc);
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__) || defined(__EMSCRIPTEN__)
       #else
             XTransform transform =
             {
@@ -2242,7 +1810,7 @@ class OpenGLDisplayDriver : DisplayDriver
             XRenderSetPictureTransform(xGlobalDisplay, oglDisplay.pixmapPicture, &transform);
             XRenderComposite(xGlobalDisplay, PictOpSrc, oglDisplay.pixmapPicture, None, oglDisplay.shapePicture, 0, 0, 0, 0, 0, 0, display.width, display.height);
             XRenderComposite(xGlobalDisplay, PictOpSrc, oglDisplay.pixmapPicture, None, oglDisplay.windowPicture, 0, 0, 0, 0, 0, 0, display.width, display.height);
-            #if !defined(__APPLE__) && !defined(__OLDX__)
+            #if !defined(__APPLE__)
             XShapeCombineMask(xGlobalDisplay, (X11Window)display.window, ShapeInput, 0, 0, oglDisplay.shapePixmap, ShapeSet);
             #else
             XShapeCombineMask(xGlobalDisplay, display.window, 2, 0, 0, oglDisplay.shapePixmap, ShapeSet);
@@ -2258,15 +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__)
-         eglSwapBuffers(eglDisplay, eglSurface);
+      #if defined(__ANDROID__) || defined(__ODROID__)
+         egl_swap_buffers();
+      #elif defined(__EMSCRIPTEN__)
       #else
          glXSwapBuffers(xGlobalDisplay, (GLXDrawable)display.window);
       #endif
 #endif
       }
-      //Logf("Out of DisplayScreen\n");
    }
 
    void FreeBitmap(DisplaySystem displaySystem, Bitmap bitmap)
@@ -2283,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);
@@ -2307,7 +1877,10 @@ 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);
 
-      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);
 
@@ -2325,18 +1898,28 @@ 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 = { };
+         convBitmap.Copy(bitmap);
+      }
 
       // Pre process the bitmap... First make it 32 bit
-      if(/*bitmap.pixelFormat == pixelFormatRGBA || */bitmap.Convert(null, pixelFormat888, null))
+      if(/*bitmap.pixelFormat == pixelFormatRGBA || */convBitmap.Convert(null, pixelFormat888, null))
       {
          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);
@@ -2353,51 +1936,105 @@ 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:
-               ColorAlpha color = ((ColorAlpha *)bitmap.picture)[c];
-               ((ColorRGBA *)bitmap.picture)[c] = ColorRGBA { color.color.r, color.color.g, color.color.b, color.a };
+               ColorAlpha color = ((ColorAlpha *)convBitmap.picture)[c];
+               ((ColorRGBA *)convBitmap.picture)[c] = ColorRGBA { color.color.r, color.color.g, color.color.b, color.a };
+            }
+         }
+         // 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;
             }
          }
-         bitmap.pixelFormat = pixelFormat888;
 
          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, GL_LINEAR);
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+         //glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP);
+         //glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP);
 
-         //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_TO_EDGE);
+         glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
-         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-         glTexParameteri(GL_TEXTURE_2D, 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
+
+#ifdef GL_TEXTURE_MAX_ANISOTROPY_EXT
+         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0 );
+#endif
 
+#if ENABLE_GL_FFP
+      if(!capabilities.shaders)
          glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+#endif
 
          result = true;
 
-         for(level = 0; result && (w > 1 || h > 1); level++, w >>= 1, h >>= 1)
+         for(level = 0; result && (w >= 1 || h >= 1); level++, w >>= 1, h >>= 1)
          {
             Bitmap mipMap;
+            if(!w) w = 1;
+            if(!h) h = 1;
             if(bitmap.width != w || bitmap.height != h)
             {
                mipMap = Bitmap { };
-               if(mipMap.Allocate(null, w, h, w, bitmap.pixelFormat, false))
+               if(mipMap.Allocate(null, w, h, w, convBitmap.pixelFormat, false))
                {
                   Surface mipSurface = mipMap.GetSurface(0,0,null);
-                  mipSurface.Filter(bitmap, 0,0,0,0, w, h, bitmap.width, bitmap.height);
+                  mipSurface.blend = false;
+                  mipSurface.Filter(convBitmap, 0,0,0,0, w, h, convBitmap.width, convBitmap.height);
                   delete mipSurface;
                }
                else
@@ -2407,7 +2044,7 @@ class OpenGLDisplayDriver : DisplayDriver
                }
             }
             else
-               mipMap = bitmap;
+               mipMap = convBitmap;
 
             if(result)
             {
@@ -2415,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);
@@ -2426,22 +2063,23 @@ class OpenGLDisplayDriver : DisplayDriver
                   result = false;
                }
             }
-            if(mipMap != bitmap)
+            if(mipMap != convBitmap)
                delete mipMap;
             if(!mipMaps) break;
          }
 
-         if(!bitmap.keepData)
-            bitmap.driver.FreeBitmap(bitmap.displaySystem, bitmap);
+         convBitmap.driver.FreeBitmap(convBitmap.displaySystem, convBitmap);
          bitmap.driverData = (void *)(uintptr)glBitmap;
          bitmap.driver = displaySystem.driver;
+         if(bitmap.keepData)
+            delete convBitmap;
 
          if(!result)
             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;
          }
       }
@@ -2463,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;
@@ -2502,8 +2139,6 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       Box box;
 
-      //Logf("Clip\n");
-
       if(clip != null)
       {
          box = clip;
@@ -2540,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...
@@ -2558,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;
@@ -2573,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;
@@ -2600,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)
@@ -2634,30 +2263,26 @@ class OpenGLDisplayDriver : DisplayDriver
       x2 += surface.offset.x;
       y2 += surface.offset.y;
 
-      //Logf("Line\n");
-
-      glColor4fv(oglSurface.foreground);
-      glBegin(GL_LINES);
-#ifdef __ANDROID__
+      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)
@@ -2668,63 +2293,58 @@ class OpenGLDisplayDriver : DisplayDriver
       x2 += surface.offset.x;
       y2 += surface.offset.y;
 
-      //Logf("Rectangle\n");
-
-      glColor4fv(oglSurface.foreground);
-#ifdef __ANDROID__
+      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);
-      glRecti(x1+surface.offset.x, y1+surface.offset.y,
-              x2+surface.offset.x + 1, y2+surface.offset.y + 1);
+      GLColor4fv(oglSurface.background);
 
+      GLRecti(x1+surface.offset.x, y1+surface.offset.y,
+              x2+surface.offset.x + 1, y2+surface.offset.y + 1);
       /*
-      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);
       */
    }
@@ -2757,73 +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;
-
-#if !defined(__OLDX__)
-         // WHY DO WE HAVE GL_ONE HERE ?
-         /*if(glBlendFuncSeparate && !oglSurface.writingText)
-            glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);*/
-#endif
+      GLuint tex = (GLuint)(uintptr)bitmap.driverData;
+      if(!tex) return;
 
       if(!oglSurface.writingText)
       {
          // glTranslatef(-0.375f, -0.375f, 0.0f);
-         glEnable(GL_TEXTURE_2D);
-         glColor4fv(oglSurface.bitmapMult);
+         GLSetupTexturing(true);
+         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(GL_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)
       {
-         glDisable(GL_TEXTURE_2D);
-
+         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);
-
-#if !defined(__OLDX__)
-         /*if(glBlendFuncSeparate && !oglSurface.writingText)
-            glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);*/
-#endif
    }
 
    void Stretch(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
@@ -2832,59 +2444,47 @@ class OpenGLDisplayDriver : DisplayDriver
 
       //glTranslate(-0.375, -0.375, 0.0);
 
-      //Logf("Stretch\n");
-
-#if !defined(__OLDX__)
-      /*if(glBlendFuncSeparate)
-         glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);*/
-#endif
-
-      glEnable(GL_TEXTURE_2D);
+      GLSetupTexturing(true);
       glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)bitmap.driverData);
 
-      glColor4fv(oglSurface.bitmapMult);
+      GLColor4fv(oglSurface.bitmapMult);
 
-      glBegin(GL_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();
 
-      glDisable(GL_TEXTURE_2D);
+      GLSetupTexturing(false);
 
       //glTranslate(0.375, 0.375, 0.0);
-#if !defined(__OLDX__)
-      /*if(glBlendFuncSeparate)
-         glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);*/
-#endif
-
    }
 
    void Filter(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
@@ -2897,8 +2497,6 @@ class OpenGLDisplayDriver : DisplayDriver
       float s2dw,s2dh,d2sw,d2sh;
       //bool flipX = false, flipY = false;
 
-      //Logf("StretchDI\n");
-
       if(Sgn(w) != Sgn(sw))
       {
          w = Abs(w);
@@ -2978,24 +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);
-         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);
       }
    }
 
    void BlitDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h)
    {
-      //Logf("BlitDI\n");
-
       //Clip against the edges of the source
       if(sx<0)
       {
@@ -3043,16 +2644,21 @@ 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);
-         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);
       }
    }
 
@@ -3066,49 +2672,70 @@ class OpenGLDisplayDriver : DisplayDriver
       ((subclass(DisplayDriver))class(LFBDisplayDriver)).UnloadFont(displaySystem, font);
    }
 
-   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags)
+   Font LoadFont(DisplaySystem displaySystem, const char * faceName, float size, FontFlags flags, float outlineSize, float outlineFade)
    {
       Font font;
       OGLSystem oglSystem = displaySystem.driverData;
       oglSystem.loadingFont = true;
-      font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags);
+      font = ((subclass(DisplayDriver))class(LFBDisplayDriver)).LoadFont(displaySystem, faceName, size, flags, outlineSize, outlineFade);
       return font;
    }
 
-   void FontExtent(DisplaySystem displaySystem, Font font, const char * text, int len, int * width, int * height)
+   void FontExtent(DisplaySystem displaySystem, Font font, const char * text, int len, int * width, int * height, int prevGlyph, int * rPrevGlyph, int * adv)
    {
-      ((subclass(DisplayDriver))class(LFBDisplayDriver)).FontExtent(displaySystem, font, text, len, width, height);
+      ((subclass(DisplayDriver))class(LFBDisplayDriver)).FontExtent(displaySystem, font, text, len, width, height, prevGlyph, rPrevGlyph, adv);
    }
 
-   void WriteText(Display display, Surface surface, int x, int y, const char * text, int len)
+   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);
 
-      //Logf("Blit\n");
+         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);
+         }
 
-      if(surface.textOpacity)
-      {
-         int w, h;
-         FontExtent(display.displaySystem, surface.font, text, len, &w, &h);
-         display.displaySystem.driver.Area(display, surface,x,y,x+w-1,y+h-1);
-      }
+         oglSurface.writingText = true;
+
+         GLSetupTexturing(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);
 
-      oglSurface.writingText = true;
+         lastBlitTex = 0;
+         oglSurface.font.ProcessString(surface.displaySystem, (const byte *)text, len, true, surface, display, &x, y, prevGlyph, rPrevGlyph, null);
 
-      glEnable(GL_TEXTURE_2D);
-      glColor4fv(oglSurface.foreground);
+         if(lastBlitTex) GLEnd();
+         lastBlitTex = 0;
 
-      ((subclass(DisplayDriver))class(LFBDisplayDriver)).WriteText(display, surface, x, y, text, len);
-      oglSurface.writingText = false;
-      oglSystem.loadingFont = false;
+         oglSurface.writingText = false;
+         oglSystem.loadingFont = false;
 
-      glDisable(GL_TEXTURE_2D);
+         GLSetupTexturing(false);
 
-      //glTranslated(0.375, 0.375, 0.0);
+         //glTranslated(0.375, 0.375, 0.0);
+      }
    }
 
    void TextFont(Display display, Surface surface, Font font)
@@ -3122,12 +2749,12 @@ class OpenGLDisplayDriver : DisplayDriver
       oglSurface.opaqueText = opaque;
    }
 
-   void TextExtent(Display display, Surface surface, const char * text, int len, int * width, int * height)
+   void TextExtent(Display display, Surface surface, const char * text, int len, int * width, int * height, int prevGlyph, int * rPrevGlyph, int * adv)
    {
       OGLSurface oglSurface = surface.driverData;
       OGLSystem oglSystem = display.displaySystem.driverData;
       oglSystem.loadingFont = true;
-      FontExtent(display.displaySystem, oglSurface.font, text, len, width, height);
+      FontExtent(display.displaySystem, oglSurface.font, text, len, width, height, prevGlyph, rPrevGlyph, adv);
       oglSystem.loadingFont = false;
    }
 
@@ -3138,47 +2765,75 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void LineStipple(Display display, Surface surface, uint32 stipple)
    {
-      //Logf("Stipple\n");
-
       if(stipple)
       {
-#if defined(__ANDROID__)
-         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(__ANDROID__)
-         stippleEnabled = false;
-         glMatrixMode(GL_TEXTURE);
-         glLoadIdentity();
-         glMatrixMode(GL_PROJECTION);
-         glDisable(GL_TEXTURE_2D);
-#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:
-            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:
             if(value) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
@@ -3190,19 +2845,47 @@ class OpenGLDisplayDriver : DisplayDriver
          case fogColor:
          {
             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:
-            glFogf(GL_FOG_DENSITY, (float)(RenderStateFloat { 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:
          {
-            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;
          }
          case alphaWrite:
@@ -3213,7 +2896,8 @@ class OpenGLDisplayDriver : DisplayDriver
          case vSync:
          {
 #if defined(__WIN32__)
-            wglSwapIntervalEXT(value ? 1 : 0);
+            if(wglSwapIntervalEXT)
+               wglSwapIntervalEXT(value ? 1 : 0);
 #endif
             break;
          }
@@ -3222,165 +2906,169 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void SetLight(Display display, int id, Light light)
    {
-      //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(camera)
+      if(surface && camera)
       {
          int left = surface.box.left + surface.offset.x;
          int top = surface.box.top  + surface.offset.y;
@@ -3393,165 +3081,641 @@ class OpenGLDisplayDriver : DisplayDriver
          int w = right - left + 1;
          int h = bottom - top + 1;
 
-         // *** ViewPort ***
-         glViewport(x, y, w, h);
+         // *** ViewPort ***
+         glViewport(x, y, w, h);
+
+         GLMatrixMode(MatrixMode::texture);
+         if(!display.display3D.camera)
+            GLPushMatrix();
+         GLLoadIdentity();
+
+         // *** Projection Matrix ***
+         GLMatrixMode(MatrixMode::projection);
+         if(!display.display3D.camera)
+            GLPushMatrix();
+
+         if(display.display3D.collectingHits)
+         {
+            float pickX = display.display3D.pickX + surface.offset.x;
+            float pickY = display.height - (display.display3D.pickY + surface.offset.y) - 1;
+            Matrix pickMatrix
+            {
+               {
+                  w / display.display3D.pickWidth, 0, 0, 0,
+                  0, h / display.display3D.pickHeight, 0, 0,
+                  0, 0, 1, 0,
+                  (w + 2.0f * (x - pickX)) / display.display3D.pickWidth,
+                  (h + 2.0f * (y - pickY)) / display.display3D.pickHeight, 0, 1
+               }
+            };
+            GLLoadMatrixd(pickMatrix.array);
+         }
+         else
+            GLLoadIdentity();
+         GLFrustum(
+            (left   - origX) * camera.zMin / camera.focalX,
+            (right  - origX) * camera.zMin / camera.focalX,
+            (bottom - origY) * camera.zMin / camera.focalY,
+            (top    - origY) * camera.zMin / camera.focalY,
+            camera.zMin, camera.zMax);
+
+         glDisable(GL_BLEND);
+
+         // *** Z Inverted Identity Matrix ***
+         GLMatrixMode(MatrixMode::modelView);
+         if(!display.display3D.camera)
+            GLPushMatrix();
+
+         GLLoadIdentity();
+
+         GLScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
+
+         // *** View Matrix ***
+         GLMultMatrixd(camera.viewMatrix.array);
+
+#if ENABLE_GL_SHADERS
+         if(glCaps_shaders)
+         {
+            defaultShader.select();
+            defaultShader.setCamera(camera);
+         }
+#endif
+
+         // *** Lights ***
+         // ...
+
+         glEnable(GL_DEPTH_TEST);
+
+         GLSetupLighting(true);
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            glShadeModel(GL_SMOOTH);
+#endif
+         glDepthMask((byte)bool::true);
+         oglDisplay.depthWrite = true;
+
+#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);
+
+         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
+
+         // *** Restore 2D MODELVIEW Matrix ***
+         GLMatrixMode(MatrixMode::modelView);
+         GLPopMatrix();
+
+         // *** Restore 2D TEXTURE Matrix ***
+         GLMatrixMode(MatrixMode::texture);
+         GLPopMatrix();
+
+         // *** Restore 2D PROJECTION Matrix ***
+         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)
+   {
+      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(flags.doubleSided)
+      {
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, !flags.singleSideLight);
+#endif
+         glDisable(GL_CULL_FACE);
+      }
+      else
+      {
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
+            GLLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false);
+#endif
+         glEnable(GL_CULL_FACE);
+      }
+
+      // 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(flags.cubeMap || (material.baseMap && (mesh.texCoords || mesh.flags.texCoords1)))
+      {
+         Bitmap map = material.baseMap;
+         int diffuseTarget = flags.cubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
+
+#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(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(diffuseTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
+         }
+         else
+         {
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(diffuseTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+         }
+      }
+      else
+      {
+         GLSetupTexturing(false);
+      }
+
+#if ENABLE_GL_FFP && !defined(_GLES)
+      if(!glCaps_shaders)
+      {
+         int separate = material.flags.separateSpecular ? GL_SEPARATE_SPECULAR_COLOR : GL_SINGLE_COLOR;
+         if(separate != lastSeparate)
+         {
+            GLLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, separate);
+            lastSeparate = separate;
+         }
+      }
+#endif
 
-         // *** Projection Matrix ***
-         if(!display.display3D.camera)
-            glPushMatrix();
-         else
-            glMatrixMode(GL_PROJECTION);
-         if(display.display3D.collectingHits)
+      if((flags.cubeMap && material.baseMap) ||
+         (mesh.texCoords && (material.baseMap || material.bumpMap || material.specularMap || material.reflectMap)))
+      {
+#if ENABLE_GL_FFP
+         if(!glCaps_shaders)
          {
-            float pickX = display.display3D.pickX + surface.offset.x;
-            float pickY = display.height - (display.display3D.pickY + surface.offset.y) - 1;
-            Matrix pickMatrix
-            {
-               {
-                  w / display.display3D.pickWidth, 0, 0, 0,
-                  0, h / display.display3D.pickHeight, 0, 0,
-                  0, 0, 1, 0,
-                  (w + 2.0f * (x - pickX)) / display.display3D.pickWidth,
-                  (h + 2.0f * (y - pickY)) / display.display3D.pickHeight, 0, 1
-               }
-            };
-            glLoadMatrixd(pickMatrix.array);
+            glActiveTexture(GL_TEXTURE0 + tmu - 1);
+            glClientActiveTexture(GL_TEXTURE0 + tmu - 1);
          }
-         else
-            glLoadIdentity();
-         glFrustum(
-            (left   - origX) * camera.zMin / camera.focalX,
-            (right  - origX) * camera.zMin / camera.focalX,
-            (bottom - origY) * camera.zMin / camera.focalY,
-            (top    - origY) * camera.zMin / camera.focalY,
-            camera.zMin, camera.zMax);
+#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)
+         {
+            glActiveTexture(GL_TEXTURE0);
+            glClientActiveTexture(GL_TEXTURE0);
+         }
+#endif
+      }
 
-         glDisable(GL_BLEND);
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
+      {
+         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);
 
-         // *** Z Inverted Identity Matrix ***
-         glMatrixMode(GL_MODELVIEW);
-         if(!display.display3D.camera)
-            glPushMatrix();
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
 
-         glLoadIdentity();
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
 
-         glScaled(1.0/nearPlane, 1.0/nearPlane, -1.0/nearPlane);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
 
-         // *** View Matrix ***
-         glMultMatrixd(camera.viewMatrix.array);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
 
-         // *** Lights ***
-         // ...
+            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);
+            }
 
-         glEnable(GL_DEPTH_TEST);
-         glEnable(GL_LIGHTING);
-         glShadeModel(GL_SMOOTH);
-         glDepthMask((byte)bool::true);
-         oglDisplay.depthWrite = true;
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
 
-         glEnable(GL_MULTISAMPLE_ARB);
-      }
-      else if(display.display3D.camera)
-      {
-         oglDisplay.depthWrite = false;
-         glViewport(0,0,display.width,display.height);
+            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);
+         }
 
-         glDisable(GL_CULL_FACE);
-         glDisable(GL_DEPTH_TEST);
-         glDisable(GL_LIGHTING);
-         glDisable(GL_FOG);
-         glDisable(GL_TEXTURE_2D);
-         glShadeModel(GL_FLAT);
-         glEnable(GL_BLEND);
-         glDisable(GL_MULTISAMPLE_ARB);
+         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);
 
-         // *** Restore 2D MODELVIEW Matrix ***
-         glPopMatrix();
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
 
-         // *** Restore 2D PROJECTION Matrix ***
-         glMatrixMode(GL_PROJECTION);
-         glPopMatrix();
-      }
+            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);
 
-      GLBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
-   }
+            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);
+            }
 
-   void ApplyMaterial(Display display, Material material, Mesh mesh)
-   {
-      //Logf("ApplyMaterial\n");
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color );
 
-      // Basic Properties
-      if(material.flags.doubleSided)
-      {
-         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, !material.flags.singleSideLight);
-         glDisable(GL_CULL_FACE);
-      }
-      else
-      {
-         glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false);
-         glEnable(GL_CULL_FACE);
+            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
 
-      // Fog
-      if(material.flags.noFog)
-         glDisable(GL_FOG);
-      else
-         glEnable(GL_FOG);
-
-      // Maps
-      if(material.baseMap && mesh.texCoords)
+#if ENABLE_GL_FFP
+      if(!glCaps_shaders)
       {
-         Bitmap map = material.baseMap;
-         glEnable(GL_TEXTURE_2D);
-         glBindTexture(GL_TEXTURE_2D, (GLuint)(uintptr)map.driverData);
+         disableRemainingTMUs(display, tmu);
 
-         glMatrixMode(GL_TEXTURE);
-         glLoadIdentity();
-         if(material.uScale && material.vScale)
-            glScalef(material.uScale, material.vScale, 1);
-         glMatrixMode(GL_MODELVIEW);
-
-         if(material.flags.tile)
+         if(mesh.flags.colors)
          {
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+            GLColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
+            glEnable(GL_COLOR_MATERIAL);
          }
          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);
+            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
-         glDisable(GL_TEXTURE_2D);
-
-      if(mesh.flags.colors)
-      {
-         glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
-         glEnable(GL_COLOR_MATERIAL);
-      }
-      else
-      {
-         glDisable(GL_COLOR_MATERIAL);
+         else
          {
-            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] = { 0,0,0,0 };
+            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
          }
          {
-            float color[4] = { material.ambient.r, material.ambient.g, material.ambient.b, 0 };
-            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, 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);
       }
-      {
-         float color[4] = { material.specular.r, material.specular.g, material.specular.b, 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
    }
 
    void FreeMesh(DisplaySystem displaySystem, Mesh mesh)
@@ -3559,61 +3723,55 @@ class OpenGLDisplayDriver : DisplayDriver
       OGLMesh oglMesh = mesh.data;
       if(oglMesh)
       {
+         OGLSystem oglSystem = displaySystem.driverData;
+         GLCapabilities caps = glCaps;
+         SETCAPS(oglSystem.capabilities);
+
          if(!mesh.flags.vertices)
          {
-            if(oglMesh.vertices)
-            {
-               GLDeleteBuffers(1, &oglMesh.vertices);
-               oglMesh.vertices = 0;
-            }
+            oglMesh.vertices.free();
             delete mesh.vertices;
          }
          if(!mesh.flags.normals)
          {
-            if(oglMesh.normals)
-            {
-               GLDeleteBuffers(1, &oglMesh.normals);
-               oglMesh.normals = 0;
-            }
+            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)
          {
-            if(oglMesh.texCoords)
-            {
-               GLDeleteBuffers(1, &oglMesh.texCoords);
-               oglMesh.texCoords = 0;
-            }
+            oglMesh.texCoords.free();
             delete mesh.texCoords;
          }
          if(!mesh.flags.texCoords2)
          {
-            if(oglMesh.texCoords2)
-            {
-               GLDeleteBuffers(1, &oglMesh.texCoords2);
-               oglMesh.texCoords2 = 0;
-            }
-            /*
-            delete mesh.texCoords2;
-            */
+            oglMesh.texCoords2.free();
+            // delete mesh.texCoords2;
          }
          if(!mesh.flags.colors)
          {
-            if(oglMesh.colors)
-            {
-               GLDeleteBuffers(1, &oglMesh.colors);
-               oglMesh.colors = 0;
-            }
+            oglMesh.colors.free();
+            delete mesh.colors;
          }
          if(!mesh.flags)
          {
             delete oglMesh;
             mesh.data = null;
          }
+         SETCAPS(caps);
       }
    }
 
-   bool AllocateMesh(DisplaySystem displaySystem, Mesh mesh)
+   bool AllocateMesh(DisplaySystem displaySystem, Mesh mesh, MeshFeatures flags, int nVertices)
    {
       bool result = false;
 
@@ -3621,27 +3779,85 @@ class OpenGLDisplayDriver : DisplayDriver
          mesh.data = OGLMesh { };
       if(mesh.data)
       {
-         OGLMesh oglMesh = mesh.data;
-
-         if(mesh.flags.vertices && !oglMesh.vertices && !mesh.vertices)
-         {
-            mesh.vertices = mesh.flags.doubleVertices ? (Vector3Df *)new Vector3D[mesh.nVertices] : new Vector3Df[mesh.nVertices];
-            GLGenBuffers(1, &oglMesh.vertices);
-         }
-         if(mesh.flags.normals && !oglMesh.normals && !mesh.normals)
+         if(mesh.nVertices == nVertices)
          {
-            GLGenBuffers( 1, &oglMesh.normals);
-            mesh.normals = mesh.flags.doubleNormals ? (Vector3Df *)new Vector3D[mesh.nVertices] : new Vector3Df[mesh.nVertices];
-         }
-         if(mesh.flags.texCoords1 && !oglMesh.texCoords && !mesh.texCoords)
-         {
-            GLGenBuffers( 1, &oglMesh.texCoords);
-            mesh.texCoords = new Pointf[mesh.nVertices];
+            // Same number of vertices, adding features (Leaves the other features pointers alone)
+            if(mesh.flags != flags)
+            {
+               if(!mesh.flags.vertices && flags.vertices)
+               {
+                  if(flags.doubleVertices)
+                  {
+                     mesh.vertices = (Vector3Df *)new Vector3D[nVertices];
+                  }
+                  else
+                     mesh.vertices = new Vector3Df[nVertices];
+               }
+               if(!mesh.flags.normals && flags.normals)
+               {
+                  if(flags.doubleNormals)
+                  {
+                     mesh.normals = (Vector3Df *)new Vector3D[nVertices];
+                  }
+                  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];
+               }
+               if(!mesh.flags.colors && flags.colors)
+               {
+                  mesh.colors = new ColorRGBAf[nVertices];
+               }
+            }
          }
-         if(mesh.flags.colors && !oglMesh.colors && !mesh.colors)
+         else
          {
-            GLGenBuffers( 1, &oglMesh.colors);
-            mesh.colors = new ColorRGBAf[mesh.nVertices];
+            // New number of vertices, reallocate all current and new features
+            flags |= mesh.flags;
+            if(flags.vertices)
+            {
+               if(flags.doubleVertices)
+               {
+                  mesh.vertices = (Vector3Df *)renew mesh.vertices Vector3D[nVertices];
+               }
+               else
+                  mesh.vertices = renew mesh.vertices Vector3Df[nVertices];
+            }
+            if(flags.normals)
+            {
+               if(flags.doubleNormals)
+               {
+                  mesh.normals = (Vector3Df *)renew mesh.normals Vector3D[nVertices];
+               }
+               else
+                  mesh.normals = renew mesh.normals Vector3Df[nVertices];
+            }
+            if(flags.texCoords1)
+            {
+               mesh.texCoords = renew mesh.texCoords Pointf[nVertices];
+            }
+            if(flags.colors)
+            {
+               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;
       }
@@ -3650,55 +3866,59 @@ 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)
       {
-         if(!(flags.vertices) || oglMesh.vertices)
-         {
-            GLBindBuffer(GL_ARRAY_BUFFER_ARB, oglMesh.vertices);
-            GLBufferData( mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT, GL_ARRAY_BUFFER_ARB, mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices, GL_STATIC_DRAW_ARB );
-         }
+         OGLMesh oglMesh = mesh.data;
+         if(!flags) flags = mesh.flags;
+         if(flags.vertices)
+            oglMesh.vertices.allocate(
+               mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices, staticDraw);
 
-         if(!(flags.normals) || oglMesh.normals)
-         {
-            GLBindBuffer(GL_ARRAY_BUFFER_ARB, oglMesh.normals);
-            GLBufferData( mesh.flags.doubleNormals ? GL_DOUBLE : GL_FLOAT, GL_ARRAY_BUFFER_ARB, mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals, GL_STATIC_DRAW_ARB );
-         }
+         if(flags.normals)
+            oglMesh.normals.allocate(
+               mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals, staticDraw);
 
-         if(!(flags.texCoords1) || oglMesh.texCoords)
-         {
-            GLBindBuffer(GL_ARRAY_BUFFER_ARB, oglMesh.texCoords);
-            GLBufferData( GL_FLOAT, GL_ARRAY_BUFFER_ARB, mesh.nVertices * sizeof(Pointf), mesh.texCoords, GL_STATIC_DRAW_ARB );
-         }
+         if(flags.texCoords1)
+            oglMesh.texCoords.allocate(
+               mesh.nVertices * sizeof(Pointf), mesh.texCoords, staticDraw);
 
-         if(!(flags.colors) || oglMesh.colors)
-         {
-            GLBindBuffer( GL_ARRAY_BUFFER_ARB, oglMesh.colors);
-            GLBufferData( GL_FLOAT, GL_ARRAY_BUFFER_ARB, mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, GL_STATIC_DRAW_ARB );
-         }
+         if(flags.colors)
+            oglMesh.colors.allocate(
+               mesh.nVertices * sizeof(ColorRGBAf), mesh.colors, staticDraw);
 
-         GLBindBuffer( GL_ARRAY_BUFFER_ARB, 0);
+         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)
    {
       bool result = true;
 
-       return result;
+      return result;
    }
 
    void FreeIndices(DisplaySystem displaySystem, OGLIndices oglIndices)
    {
+      OGLSystem oglSystem = displaySystem.driverData;
+      GLCapabilities caps = glCaps;
+      SETCAPS(oglSystem.capabilities);
+
       if(oglIndices)
       {
-         if(oglIndices.buffer)
-            GLDeleteBuffers(1, &oglIndices.buffer);
+         oglIndices.buffer.free();
          delete oglIndices.indices;
          delete oglIndices;
       }
+      SETCAPS(caps);
    }
 
    void * AllocateIndices(DisplaySystem displaySystem, int nIndices, bool indices32bit)
@@ -3707,7 +3927,6 @@ class OpenGLDisplayDriver : DisplayDriver
       if(oglIndices)
       {
          oglIndices.indices = (void *)(indices32bit ? new uint32[nIndices] : new uint16[nIndices]);
-         GLGenBuffers( 1, &oglIndices.buffer);
          oglIndices.nIndices = nIndices;
       }
       return oglIndices;
@@ -3715,13 +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)
       {
-         GLBindBuffer( GL_ELEMENT_ARRAY_BUFFER_ARB, oglIndices.buffer);
-         GLBufferData( indices32bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, GL_ELEMENT_ARRAY_BUFFER_ARB, nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)),
-            oglIndices.indices, GL_STATIC_DRAW_ARB);
-         GLBindBuffer( GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+         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);
+
+            {
+               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
+            oglIndices.buffer.allocate(
+               nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)),
+               oglIndices.indices, staticDraw);
       }
+      SETCAPS(caps);
    }
 
    uint16 * LockIndices(DisplaySystem displaySystem, OGLIndices oglIndices)
@@ -3732,13 +3979,11 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void SelectMesh(Display display, Mesh mesh)
    {
-      //Logf("SelectMesh\n");
-
-#if !defined( __ANDROID__) && !defined(__APPLE__)
+#if !defined( __ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
 #if defined(__WIN32__)
       if(glUnlockArraysEXT)
 #endif
-         if(display.display3D.mesh)
+         if(!glCaps_vertexBuffer && display.display3D.mesh)
             glUnlockArraysEXT();
 #endif
       if(mesh)
@@ -3746,140 +3991,177 @@ class OpenGLDisplayDriver : DisplayDriver
          OGLMesh oglMesh = mesh.data;
 
          // *** Vertex Stream ***
-         glEnableClientState(GL_VERTEX_ARRAY);
+         GLEnableClientState(VERTICES);
          if(!display.display3D.collectingHits && oglMesh)
          {
-            GLBindBuffer(GL_ARRAY_BUFFER_ARB, oglMesh.vertices );
-            glVertexPointer(3, mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT, 0, vboAvailable ? null : mesh.vertices);
+            oglMesh.vertices.use(vertex, 3, (mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT), 0, oglMesh.vertices.buffer ? null : (double *)mesh.vertices);
 
             // *** Normals Stream ***
-            if(mesh.normals)
+            if(mesh.normals || mesh.flags.normals)
             {
-               glEnableClientState(GL_NORMAL_ARRAY);
-               GLBindBuffer(GL_ARRAY_BUFFER_ARB, oglMesh.normals);
-               glNormalPointer(mesh.flags.doubleNormals ? GL_DOUBLE : GL_FLOAT, 0, vboAvailable ? null : mesh.normals);
+               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)
+            if(mesh.texCoords || mesh.flags.texCoords1)
             {
-               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-               GLBindBuffer( GL_ARRAY_BUFFER_ARB, oglMesh.texCoords);
-               glTexCoordPointer(2, GL_FLOAT, 0, vboAvailable ? null : mesh.texCoords);
+               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)
+            if(mesh.colors || mesh.flags.colors)
             {
-               glEnableClientState(GL_COLOR_ARRAY);
-               GLBindBuffer( GL_ARRAY_BUFFER_ARB, oglMesh.colors);
-               glColorPointer(4, GL_FLOAT, 0, vboAvailable ? null : mesh.colors);
+               GLEnableClientState(COLORS);
+               oglMesh.colors.use(color, 4, GL_FLOAT, 0, oglMesh.colors.buffer ? null : mesh.colors);
             }
             else
-               glDisableClientState(GL_COLOR_ARRAY);
-
+               GLDisableClientState(COLORS);
          }
          else
          {
-            GLBindBuffer( GL_ARRAY_BUFFER_ARB, 0);
-            glVertexPointer(3,mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT,0,mesh.vertices);
-            if(mesh.normals && !display.display3D.collectingHits)
+            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(NORMALS);
+               noAB.use(normal, 3, GL_FLOAT, 0, mesh.normals);
+            }
+            else
+               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_NORMAL_ARRAY);
-               glNormalPointer(mesh.flags.doubleNormals ? GL_DOUBLE : GL_FLOAT, 0, mesh.normals);
+               GLEnableClientState(TEXCOORDS);
+               noAB.use(texCoord, 2, GL_FLOAT, 0, mesh.texCoords);
             }
             else
-               glDisableClientState(GL_NORMAL_ARRAY);
-            if(mesh.texCoords && !display.display3D.collectingHits)
+               GLDisableClientState(TEXCOORDS);
+
+#if ENABLE_GL_FFP
+            if(!glCaps_shaders)
             {
-               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-               glTexCoordPointer(2, GL_FLOAT, 0, mesh.texCoords);
+               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
-               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-            if(mesh.colors && !display.display3D.collectingHits)
+#endif
+            if((mesh.colors || mesh.flags.colors) && !display.display3D.collectingHits)
             {
-               glEnableClientState(GL_COLOR_ARRAY);
-               glColorPointer(4, GL_FLOAT, 0, mesh.colors);
+               GLEnableClientState(COLORS);
+               noAB.use(color, 4, GL_FLOAT, 0, mesh.colors);
             }
             else
-               glDisableClientState(GL_COLOR_ARRAY);
+               GLDisableClientState(COLORS);
          }
 
-#if !defined(__ANDROID__) && !defined(__APPLE__)
+#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__ODROID__) && !defined(__EMSCRIPTEN__)
 
 #if defined(__WIN32__)
          if(glLockArraysEXT)
 #endif
-            glLockArraysEXT(0, mesh.nVertices);
+            if(!glCaps_vertexBuffer)
+               glLockArraysEXT(0, mesh.nVertices);
 #endif
       }
-      else
-         GLBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
    }
 
    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__
-         GLBindBuffer(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(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;
-
-            if(!display.display3D.collectingHits && vboAvailable && oglIndices)
-            {
-               GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, oglIndices.buffer);
-               glDrawElements(primitiveTypes[primitive->type.primitiveType], primitive->nIndices,
-                  primitive->type.indices32bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, 0);
-               GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
-            }
-            else
-               glDrawElements(primitiveTypes[primitive->type.primitiveType], primitive->nIndices,
-                  primitive->type.indices32bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, oglIndices ? oglIndices.indices : primitive->indices);
-         }
+            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);
       }
    }
 
    void PushMatrix(Display display)
    {
-      glPushMatrix();
+      GLPushMatrix();
    }
 
    void PopMatrix(Display display, bool setMatrix)
    {
-      glPopMatrix();
+      GLPopMatrix();
    }
 
    void SetTransform(Display display, Matrix transMatrix, bool viewSpace, bool useCamera)
@@ -3889,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]);
@@ -3909,7 +4191,7 @@ class OpenGLDisplayDriver : DisplayDriver
       matrix.m[3][1] = 0;
       matrix.m[3][2] = 0;
 
-      glMultMatrixd(matrix.array);
+      GLMultMatrixd(matrix.array);
    }
 #endif
 }
@@ -3930,11 +4212,14 @@ IS_GLGetContext(DisplaySystem displaySystem)
 #if defined(__WIN32__)
       OGLSystem system = displaySystem.driverData;
       return system.glrc;
-#elif !defined(__ANDROID__)
+#elif defined(__ANDROID__) || defined(__ODROID__)
+      return eglContext;
+#elif defined(__EMSCRIPTEN__)
       OGLSystem system = displaySystem.driverData;
-      return system.glContext;
+      return (void *)system.glc;
 #else
-      return eglContext;
+      OGLSystem system = displaySystem.driverData;
+      return system.glContext;
 #endif
    }
    return null;