ecere/gfx/drivers/OpenGL: Fixed mipmapping
[sdk] / ecere / src / gfx / drivers / OpenGLDisplayDriver.ec
index 0175a7b..7c79ca4 100644 (file)
@@ -18,7 +18,7 @@ namespace gfx::drivers;
 
 #else
 
-   #if !defined(__ANDROID__)
+   #if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
 
       #define property _property
       #define new _new
@@ -75,11 +75,57 @@ namespace gfx::drivers;
    #undef String
 #endif
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__ODROID__)
+
+#define uint _uint
+#define property _property
+#define new _new
+#define class _class
+#define Window    X11Window
+#define Cursor    X11Cursor
+#define Font      X11Font
+#define Display   X11Display
+#define Time      X11Time
+#define KeyCode   X11KeyCode
+#define Picture   X11Picture
+#define Bool      X11Bool
 
    #include <GLES/gl.h>
    #include <EGL/egl.h>
 
+#undef Bool
+#undef Picture
+#undef Window
+#undef Cursor
+#undef Font
+#undef Display
+#undef Time
+#undef KeyCode
+#undef uint
+#undef new
+#undef property
+#undef class
+
+#elif defined(__EMSCRIPTEN__)
+
+   #define property _property
+   #define uint _uint
+
+   #include <GL/gl.h>
+
+   //#include <GLES/gl.h>
+   //#include <EGL/egl.h>
+
+   //#include <GLES2/gl.h>
+   //#include <EGL/egl.h>
+
+   //#include <GLES2/gl2.h>
+   #include <GL/glfw.h>
+   #include <emscripten/emscripten.h>
+
+   #undef property
+   #undef uint
+
 #else
 
    #include <GL/gl.h>
@@ -87,13 +133,24 @@ namespace gfx::drivers;
 
 #endif
 
+#if defined(__ODROID__) && !defined(_GLES)
+#define _GLES
+#endif
+
+#if defined(__EMSCRIPTEN__)
+#define EM_MODE
+// #define _GLES
+#endif
+
+//#define EM_MODE
+
 #undef pointer
 
 import "Display"
 
 #if defined(__unix__) || defined(__APPLE__)
 
-   #ifndef __ANDROID__
+   #if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
    import "XInterface"
    #endif
 
@@ -101,6 +158,16 @@ import "Display"
 
 static double nearPlane = 1;
 
+public double glesGetNearPlane()
+{
+   return nearPlane;
+}
+
+public void glesSetNearPlane(double value)
+{
+   nearPlane = value;
+}
+
 #define glLoadMatrix glLoadMatrixd
 #define glMultMatrix glMultMatrixd
 #define glGetMatrix  glGetDoublev
@@ -364,6 +431,84 @@ static double nearPlane = 1;
 
    static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
 
+#elif defined(__ANDROID__) || defined(__ODROID__)
+
+   #define GL_FRAMEBUFFER           GL_FRAMEBUFFER_OES
+   #define GL_RENDERBUFFER          GL_RENDERBUFFER_OES
+   #define GL_COLOR_ATTACHMENT0     GL_COLOR_ATTACHMENT0_OES
+
+   #define GL_POLYGON_STIPPLE 0xFFFF
+   #define GL_LINE_STIPPLE 0xFFFF
+   #define GL_LINE 0xFFFF
+   #define GL_FILL 0xFFFF
+   #define GL_ALL_ATTRIB_BITS 0xFFFF
+   #define GL_LIGHT_MODEL_LOCAL_VIEWER 0xFFFF
+
+   #define GL_POLYGON      9
+   #define GL_QUADS        7
+
+   //#define GL_QUADS              0
+   #define GL_QUAD_STRIP         0
+   //#define GL_DOUBLE             0
+   //#define GL_UNSIGNED_INT       0
+   //#define GL_FILL               0
+   //#define GL_LINE               0
+   //#define GL_LINE_STIPPLE       0
+   #define GL_BGRA_EXT           0
+   #define GL_UNPACK_ROW_LENGTH  0
+   #define GL_UNPACK_SKIP_PIXELS 0
+   #define GL_UNPACK_SKIP_ROWS   0
+   #define GL_RGBA8              0
+   #define GL_PACK_ROW_LENGTH    0
+   #define GL_PACK_SKIP_ROWS     0
+   #define GL_PACK_SKIP_PIXELS   0
+
+#endif
+
+#if defined(ECERE_NO3D) || defined(ECERE_VANILLA)
+public union Matrix
+{
+   double array[16];
+   double m[4][4];
+
+   void Identity()
+   {
+      FillBytesBy4(this, 0, sizeof(Matrix) >> 2);
+      m[0][0]=m[1][1]=m[2][2]=m[3][3]=1;
+   }
+
+   void Transpose(Matrix source)
+   {
+      int i,j;
+      for(i=0; i<4; i++)
+         for(j=0; j<4; j++)
+            m[j][i] = source.m[i][j];
+   }
+
+   void Multiply(Matrix a, Matrix b)
+   {
+      // We need a full matrix multiplication for the Projection matrix
+      m[0][0]=a.m[0][0]*b.m[0][0] + a.m[0][1]*b.m[1][0] + a.m[0][2]*b.m[2][0] + a.m[0][3]*b.m[3][0];
+      m[0][1]=a.m[0][0]*b.m[0][1] + a.m[0][1]*b.m[1][1] + a.m[0][2]*b.m[2][1] + a.m[0][3]*b.m[3][1];
+      m[0][2]=a.m[0][0]*b.m[0][2] + a.m[0][1]*b.m[1][2] + a.m[0][2]*b.m[2][2] + a.m[0][3]*b.m[3][2];
+      m[0][3]=a.m[0][0]*b.m[0][3] + a.m[0][1]*b.m[1][3] + a.m[0][2]*b.m[2][3] + a.m[0][3]*b.m[3][3];
+
+      m[1][0]=a.m[1][0]*b.m[0][0] + a.m[1][1]*b.m[1][0] + a.m[1][2]*b.m[2][0] + a.m[1][3]*b.m[3][0];
+      m[1][1]=a.m[1][0]*b.m[0][1] + a.m[1][1]*b.m[1][1] + a.m[1][2]*b.m[2][1] + a.m[1][3]*b.m[3][1];
+      m[1][2]=a.m[1][0]*b.m[0][2] + a.m[1][1]*b.m[1][2] + a.m[1][2]*b.m[2][2] + a.m[1][3]*b.m[3][2];
+      m[1][3]=a.m[1][0]*b.m[0][3] + a.m[1][1]*b.m[1][3] + a.m[1][2]*b.m[2][3] + a.m[1][3]*b.m[3][3];
+
+      m[2][0]=a.m[2][0]*b.m[0][0] + a.m[2][1]*b.m[1][0] + a.m[2][2]*b.m[2][0] + a.m[2][3]*b.m[3][0];
+      m[2][1]=a.m[2][0]*b.m[0][1] + a.m[2][1]*b.m[1][1] + a.m[2][2]*b.m[2][1] + a.m[2][3]*b.m[3][1];
+      m[2][2]=a.m[2][0]*b.m[0][2] + a.m[2][1]*b.m[1][2] + a.m[2][2]*b.m[2][2] + a.m[2][3]*b.m[3][2];
+      m[2][3]=a.m[2][0]*b.m[0][3] + a.m[2][1]*b.m[1][3] + a.m[2][2]*b.m[2][3] + a.m[2][3]*b.m[3][3];
+
+      m[3][0]=a.m[3][0]*b.m[0][0] + a.m[3][1]*b.m[1][0] + a.m[3][2]*b.m[2][0] + a.m[3][3]*b.m[3][0];
+      m[3][1]=a.m[3][0]*b.m[0][1] + a.m[3][1]*b.m[1][1] + a.m[3][2]*b.m[2][1] + a.m[3][3]*b.m[3][1];
+      m[3][2]=a.m[3][0]*b.m[0][2] + a.m[3][1]*b.m[1][2] + a.m[3][2]*b.m[2][2] + a.m[3][3]*b.m[3][2];
+      m[3][3]=a.m[3][0]*b.m[0][3] + a.m[3][1]*b.m[1][3] + a.m[3][2]*b.m[2][3] + a.m[3][3]*b.m[3][3];
+   }
+};
 #endif
 
 // Our own matrix stack
@@ -374,29 +519,18 @@ static int curStack = 0;
 #if defined(_GLES)
 
    // OpenGL ES Porting Kit
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__ODROID__)
    #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 GL_INT                                  0x1404
    #define GL_UNSIGNED_INT                         0x1405
    #define GL_DOUBLE                               0x140A
-   #define GL_POLYGON      9
-   #define GL_QUADS        7
    #define APIENTRY
 #endif
 
@@ -421,6 +555,7 @@ static int curStack = 0;
    #define glColor4fv            glesColor4fv
    #define glLineStipple         glesLineStipple
    #define glNormal3fv           glesNormal3fv
+   #define glNormal3f            glesNormal3f
    #define glTexCoord2fv         glesTexCoord2fv
    #define glColorMaterial       glesColorMaterial
 
@@ -437,24 +572,6 @@ static int curStack = 0;
    #define glVertex3fv           glesVertex3fv
    #define glLightModeli         glesLightModeli
 
-#if defined(__ANDROID__)
-   //#define GL_QUADS              0
-   #define GL_QUAD_STRIP         0
-   //#define GL_DOUBLE             0
-   //#define GL_UNSIGNED_INT       0
-   //#define GL_FILL               0
-   //#define GL_LINE               0
-   //#define GL_LINE_STIPPLE       0
-   #define GL_BGRA_EXT           0
-   #define GL_UNPACK_ROW_LENGTH  0
-   #define GL_UNPACK_SKIP_PIXELS 0
-   #define GL_UNPACK_SKIP_ROWS   0
-   #define GL_RGBA8              0
-   #define GL_PACK_ROW_LENGTH    0
-   #define GL_PACK_SKIP_ROWS     0
-   #define GL_PACK_SKIP_PIXELS   0
-#endif
-
 #else
 
 #define glVertexPointerd(nc, s, p, nv)       glVertexPointer(nc, GL_DOUBLE, s, p)
@@ -462,13 +579,17 @@ static int curStack = 0;
 
 #endif
 
-#if defined(__ANDROID__)
+#if defined(__ANDROID__) || defined(__ODROID__)
    static EGLDisplay eglDisplay;
    static EGLSurface eglSurface;
    static EGLContext eglContext;
    static int eglWidth, eglHeight;
 
+#if defined(__ANDROID__)
    static bool egl_init_display(ANativeWindow* window)
+#else
+   static bool egl_init_display(uint window)
+#endif
    {
       const EGLint attribs[] =
       {
@@ -542,7 +663,8 @@ static int curStack = 0;
       glesLoadIdentity();
       glOrtho(0,w,h,0,0.0,1.0);
 
-      currentVertexBuffer = 0;
+      curArrayBuffer = 0;
+      curElementBuffer = 0;
       return true;
    }
 
@@ -575,9 +697,12 @@ static int vertexCount;
 static int normalCount;
 static float *vertexPointer;
 static float *normalPointer;
-static GLenum beginMode;
-static unsigned int beginBufferSize, normalBufferSize;
+static GLenum beginMode = -1;
+static uint beginBufferSize, normalBufferSize;
 static int numVertexCoords = 2;
+static bool vertexColorValues = false;
+static int vertexStride = 4;
+static int vertexOffset = 2;
 
 public void glesRecti(int a, int b, int c, int d)
 {
@@ -594,10 +719,15 @@ public void glesBegin(GLenum mode)
    beginMode = mode;
    beginCount = 0;
    vertexCount = 0;
+   vertexColorValues = false;
+   vertexOffset = 2;
+   vertexStride = 4;
+   numVertexCoords = 2;
+
    if(!vertexPointer)
    {
       normalBufferSize = beginBufferSize = 1024;  // default number of vertices
-      vertexPointer = new float[beginBufferSize * 5];
+      vertexPointer = new float[beginBufferSize * vertexStride];
       normalPointer = new float[normalBufferSize * 3];
    }
 }
@@ -609,20 +739,20 @@ public void glesTexCoord2f(float x, float y)
    if(vertexCount + numVertexCoords > beginBufferSize)
    {
       beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * 5];
+      vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
    }
 
-   vertexPointer[count*(2+numVertexCoords)  ] = x;
-   vertexPointer[count*(2+numVertexCoords)+1] = y;
+   vertexPointer[count*vertexStride  ] = x;
+   vertexPointer[count*vertexStride+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];
+      vertexPointer[count*vertexStride  ] = vertexPointer[(count-4)*vertexStride];
+      vertexPointer[count*vertexStride+1] = vertexPointer[(count-4)*vertexStride+1];
       count++;
-      vertexPointer[count*(2+numVertexCoords)  ] = vertexPointer[(count-3)*(2+numVertexCoords)];
-      vertexPointer[count*(2+numVertexCoords)+1] = vertexPointer[(count-3)*(2+numVertexCoords)+1];
+      vertexPointer[count*vertexStride  ] = vertexPointer[(count-3)*vertexStride];
+      vertexPointer[count*vertexStride+1] = vertexPointer[(count-3)*vertexStride+1];
       count++;
    }
 }
@@ -633,23 +763,25 @@ public void glesTexCoord2fv(float * a)         { glesTexCoord2f(a[0], a[1]); }
 public void glesVertex2f(float x, float y)
 {
    numVertexCoords = 2;
+   vertexStride = vertexOffset + numVertexCoords;
+
    if(vertexCount + 4 > beginBufferSize)
    {
       beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * 5];
+      vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
    }
 
-   vertexPointer[vertexCount*4+2] = x;
-   vertexPointer[vertexCount*4+3] = y;
+   vertexPointer[vertexCount*vertexStride+vertexOffset] = x;
+   vertexPointer[vertexCount*vertexStride+vertexOffset + 1] = 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];
+      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset];
+      vertexPointer[vertexCount*vertexStride+vertexOffset + 1] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset + 1];
       vertexCount++;
-      vertexPointer[vertexCount*4+2] = vertexPointer[(vertexCount-3)*4+2];
-      vertexPointer[vertexCount*4+3] = vertexPointer[(vertexCount-3)*4+3];
+      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset];
+      vertexPointer[vertexCount*vertexStride+vertexOffset + 1] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset + 1];
       vertexCount++;
    }
    beginCount++;
@@ -662,21 +794,31 @@ public 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);
+   noAB.use(texCoord, 2, GL_FLOAT, vertexStride * sizeof(float), vertexPointer);
+   if(vertexColorValues)
+   {
+      glEnableClientState(GL_COLOR_ARRAY);
+      noAB.use(color, 4, GL_FLOAT, vertexStride * sizeof(float), vertexPointer + 2);
+   }
+   noAB.use(vertex, numVertexCoords, GL_FLOAT, (vertexStride)*sizeof(float),vertexPointer+vertexOffset);
    if(normalCount && normalCount == vertexCount)
    {
       glEnableClientState(GL_NORMAL_ARRAY);
-      glNormalPointer  (GL_FLOAT, 3*sizeof(float),normalPointer);
+      noAB.use(normal, 3, GL_FLOAT, 3*sizeof(float),normalPointer);
    }
 
    glDrawArrays(mode, 0, vertexCount);
    if(normalCount)
       glDisableClientState(GL_NORMAL_ARRAY);
+   if(vertexColorValues)
+      glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    normalCount = 0;
+   vertexColorValues = false;
+   numVertexCoords = 2;
+   beginMode = -1;
 }
 
 // Vertex Pointer
@@ -735,19 +877,59 @@ public void glesTexReuseDoubleVP(int numCoords)
    glTexCoordPointer(numCoords, GL_FLOAT, 0, floatVPBuffer);
 }
 
+public void glesColor4f(float r, float g, float b, float a)
+{
+   if(beginMode != (GLenum)-1)
+   {
+      int count = vertexCount;
+
+      vertexColorValues = true;
+      vertexOffset = 6;
+      vertexStride = vertexOffset + numVertexCoords;
+
+      if(vertexCount + vertexStride > beginBufferSize)
+      {
+         beginBufferSize = beginBufferSize + beginBufferSize/2;
+         vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
+      }
+
+      vertexPointer[count*vertexStride + 2] = r;
+      vertexPointer[count*vertexStride + 3] = g;
+      vertexPointer[count*vertexStride + 4] = b;
+      vertexPointer[count*vertexStride + 5] = a;
+      count++;
+
+      if(beginMode == GL_QUADS && ((beginCount % 4) == 3))
+      {
+         vertexPointer[count*vertexStride + 2] = vertexPointer[(count-4) * vertexStride + 2];
+         vertexPointer[count*vertexStride + 3] = vertexPointer[(count-4) * vertexStride + 3];
+         vertexPointer[count*vertexStride + 4] = vertexPointer[(count-4) * vertexStride + 4];
+         vertexPointer[count*vertexStride + 5] = vertexPointer[(count-4) * vertexStride + 5];
+         count++;
+         vertexPointer[count*vertexStride + 2] = vertexPointer[(count-3) * vertexStride + 2];
+         vertexPointer[count*vertexStride + 3] = vertexPointer[(count-3) * vertexStride + 3];
+         vertexPointer[count*vertexStride + 4] = vertexPointer[(count-3) * vertexStride + 4];
+         vertexPointer[count*vertexStride + 5] = vertexPointer[(count-3) * vertexStride + 5];
+         count++;
+      }
+   }
+   else
+      glColor4f(r, g, b, a);
+}
+
 public void glesColor3f( float r, float g, float b )
 {
-   glColor4f(r, g, b, 1.0f);
+   glesColor4f(r, g, b, 1.0f);
 }
 
 public 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);
+   glesColor4f(r/255.0f, g/255.0f, b/255.0f, a/255.0f);
 }
 
 public void glesColor4fv(float * a)
 {
-   glColor4f(a[0], a[1], a[2], a[3]);
+   glesColor4f(a[0], a[1], a[2], a[3]);
 }
 
 public void glesBufferDatad(int target, int size, void * data, int usage)
@@ -870,6 +1052,7 @@ public void glesFrustum( double l, double r, double b, double t, double n, doubl
    }
 }
 
+#if !defined(ECERE_NO3D) && !defined(ECERE_VANILLA)
 public void glesRotated( double a, double b, double c, double d )
 {
    Quaternion q;
@@ -910,6 +1093,7 @@ public void glesMultMatrixd( double * i )
    matrixStack[curStack][matrixIndex[curStack]] = r;
    LoadCurMatrix();
 }
+#endif
 
 public void glesMatrixMode(int mode)
 {
@@ -988,26 +1172,28 @@ void glesMultMatrixd( double * i )
 public void glesVertex3f( float x, float y, float z )
 {
    numVertexCoords = 3;
-   if(vertexCount + 4 > beginBufferSize)
+   vertexStride = vertexOffset + numVertexCoords;
+
+   if(vertexCount + vertexStride > beginBufferSize)
    {
       beginBufferSize = beginBufferSize + beginBufferSize/2;
-      vertexPointer = renew vertexPointer float[beginBufferSize * 5];
+      vertexPointer = renew vertexPointer float[beginBufferSize * vertexStride];
    }
 
-   vertexPointer[vertexCount*5+2] = x;
-   vertexPointer[vertexCount*5+3] = y;
-   vertexPointer[vertexCount*5+4] = z;
+   vertexPointer[vertexCount*vertexStride+vertexOffset] = x;
+   vertexPointer[vertexCount*vertexStride+vertexOffset+1] = y;
+   vertexPointer[vertexCount*vertexStride+vertexOffset+2] = z;
    vertexCount++;
 
    if(beginMode == GL_QUADS && ((beginCount % 4) == 3))
    {
-      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];
+      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset];
+      vertexPointer[vertexCount*vertexStride+vertexOffset+1] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset+1];
+      vertexPointer[vertexCount*vertexStride+vertexOffset+2] = vertexPointer[(vertexCount-4)*vertexStride+vertexOffset+2];
       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];
+      vertexPointer[vertexCount*vertexStride+vertexOffset] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset];
+      vertexPointer[vertexCount*vertexStride+vertexOffset+1] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset+1];
+      vertexPointer[vertexCount*vertexStride+vertexOffset+2] = vertexPointer[(vertexCount-3)*vertexStride+vertexOffset+2];
       vertexCount++;
    }
    beginCount++;
@@ -1068,7 +1254,7 @@ public void glesTerminate()
 }
 
 static GLuint stippleTexture;
-#if defined(_GLES)
+#if defined(_GLES) || defined(EM_MODE)
 static bool stippleEnabled;
 #endif
 
@@ -1100,11 +1286,13 @@ public void glesLineStipple( int i, unsigned short j )
 
 public void glesLightModeli( unsigned int pname, int param )
 {
+#if !defined(EM_MODE)
    if(pname == GL_LIGHT_MODEL_TWO_SIDE)
       glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, param);
+#endif
 }
 
-#ifdef __ANDROID__
+#if defined(__ANDROID__) || defined(__ODROID__)
 void glClearDepth( double depth ) { glClearDepthf((float)depth); }
 void glFogi( unsigned int pname, int param ) { }
 void glPolygonMode( unsigned int i, unsigned int j ) { }
@@ -1128,53 +1316,161 @@ void glDrawPixels(int a, int b, int c, int d, void * e) { }
 
 #endif
 
-#if !defined(__APPLE__) && !defined(__WIN32__)
+#if !defined(__APPLE__) && !defined(__WIN32__) && !defined(__ODROID__)
 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
 
-static int currentVertexBuffer;
+public void GLLoadMatrix(Matrix matrix)
+{
+   float m[16] =
+   {
+      (float)matrix.m[0][0], (float)matrix.m[0][1], (float)matrix.m[0][2], (float)matrix.m[0][3],
+      (float)matrix.m[1][0], (float)matrix.m[1][1], (float)matrix.m[1][2], (float)matrix.m[1][3],
+      (float)matrix.m[2][0], (float)matrix.m[2][1], (float)matrix.m[2][2], (float)matrix.m[2][3],
+      (float)matrix.m[3][0], (float)matrix.m[3][1], (float)matrix.m[3][2], (float)matrix.m[3][3]
+   };
+   glLoadMatrixf(m);
+}
+
+public enum GLBufferContents { vertex, normal, texCoord, color };
+
+public define noAB = GLAB { 0 };
+
+static uint curArrayBuffer;
+
+public struct GLAB
+{
+   uint buffer;
+
+   void upload(uint size, void * data)
+   {
+      if(this != null)
+      {
+         if(!buffer)
+            GLGenBuffers(1, this);
+         if(curArrayBuffer != buffer)
+            GLBindBuffer(GL_ARRAY_BUFFER, buffer);
+         glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);  //GL_DYNAMIC_DRAW);
+      }
+   }
+
+   void free()
+   {
+      if(this != null)
+      {
+         GLDeleteBuffers(1, this);
+         buffer = 0;
+      }
+   }
+
+   void use(GLBufferContents contents, int n, int type, uint stride, void * pointer)
+   {
+      if(curArrayBuffer != ((this != null) ? buffer : 0))
+         GLBindBuffer(GL_ARRAY_BUFFER, ((this != null) ? buffer : 0));
+      switch(contents)
+      {
+         case normal:   glNormalPointer(type, stride, pointer); break;
+         case vertex:   glVertexPointer(n, type, stride, pointer); break;
+         case texCoord: glTexCoordPointer(n, type, stride, pointer); break;
+         case color:    glColorPointer(n, type, stride, pointer); break;
+      }
+   }
+
+   void useVertTrans(uint count, int n, int type, uint stride, void * pointer)
+   {
+#ifdef _GLES
+      if(curArrayBuffer != ((this != null) ? buffer : 0))
+         GLBindBuffer(GL_ARRAY_BUFFER, ((this != null) ? buffer : 0));
+      if(type == GL_INT)
+         glVertexPointeri(n, stride, pointer, count);
+      else if(type == GL_DOUBLE)
+         glVertexPointerd(n, stride, pointer, count);
+#else
+      use(vertex, n, type, stride, pointer);
+#endif
+   }
+};
+
+static uint curElementBuffer;
+
+public define noEAB = GLEAB { 0 };
 
-bool GLSelectVBO(uint vbo)
+public struct GLEAB
 {
-   if(currentVertexBuffer != vbo)
+   uint buffer;
+
+   void upload(uint size, void * data)
    {
-      GLBindBuffer(GL_ARRAY_BUFFER, vbo);
-      currentVertexBuffer = vbo;
-      return true;
+      if(this != null)
+      {
+         if(!buffer)
+            GLGenBuffers(1, (GLAB *)this);
+
+         if(curElementBuffer != buffer)
+            GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
+         glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);  //GL_DYNAMIC_DRAW);
+      }
    }
-   return false;
-}
 
-void GLGenBuffers(int count, uint * buffer)
+   void free()
+   {
+      if(this != null)
+      {
+         GLDeleteBuffers(1, (GLAB *)this);
+         buffer = 0;
+      }
+   }
+
+   void draw(int primType, int count, int type, void * indices)
+   {
+      if(curElementBuffer != ((this != null) ? buffer : 0))
+         GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((this != null) ? buffer : 0));
+#ifdef _GLES
+      type = GL_UNSIGNED_SHORT;
+#endif
+      glDrawElements(primType, count, type, indices);
+   }
+};
+
+public void GLGenBuffers(int count, GLAB * buffers)
 {
-#ifdef __ANDROID__
-   glGenBuffers(count, buffer);
+#if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
+   glGenBuffers(count, (GLuint *)buffers);
 #else
 #if defined(__WIN32__)
    if(glGenBuffersARB)
 #endif
-      glGenBuffersARB(count, buffer);
+      glGenBuffersARB(count, (GLuint *)buffers);
 #endif
 }
 
-void GLDeleteBuffers(int count, GLuint * buffer)
+public void GLDeleteBuffers(int count, GLAB * buffers)
 {
-#ifdef __ANDROID__
-   glDeleteBuffers(count, buffer);
+   int i;
+   for(i = 0; i < count; i++)
+   {
+      uint buffer = buffers[i].buffer;
+      if(buffer == curArrayBuffer)
+         GLBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
+      else if(buffer == curElementBuffer)
+         GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
+   }
+#if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
+   glDeleteBuffers(count, (GLuint *)buffers);
 #else
 #if defined(__WIN32__)
    if(glDeleteBuffersARB)
 #endif
-      glDeleteBuffersARB(count, buffer);
+      glDeleteBuffersARB(count, (GLuint *)buffers);
 #endif
 }
 
 void GLBindBuffer(int target, uint buffer)
 {
-#ifdef __ANDROID__
+#if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
    glBindBuffer(target, buffer);
 #else
 #if defined(__WIN32__)
@@ -1182,6 +1478,10 @@ void GLBindBuffer(int target, uint buffer)
 #endif
       glBindBufferARB(target, buffer);
 #endif
+   if(target == GL_ARRAY_BUFFER_ARB)
+      curArrayBuffer = buffer;
+   else if(target == GL_ELEMENT_ARRAY_BUFFER_ARB)
+      curElementBuffer = buffer;
 }
 
 public void GLVertexPointer(int numCoords, int glType, int stride, void *ptr, int numVertices)
@@ -1206,7 +1506,7 @@ public void GLBufferData(int type, GLenum target, int size, const GLvoid *data,
    else
 #endif
 
-#ifdef __ANDROID__
+#if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
       glBufferData(target, size, data, usage);
 #else
 
@@ -1266,7 +1566,7 @@ class OGLDisplay : struct
    int imageBuffers[2];
    byte * pboMemory1, * pboMemory2;
    */
-#elif !defined(__ANDROID__)
+#elif !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
    GLXContext glContext;
 
    Pixmap pixmap;
@@ -1300,7 +1600,7 @@ class OGLSystem : struct
    HDC hdc;
    HGLRC glrc;
    HWND hwnd;
-#elif !defined(__ANDROID__)
+#elif !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
    XVisualInfo * visualInfo;
    GLXContext glContext;
    GLXDrawable glxDrawable;
@@ -1320,17 +1620,17 @@ class OGLSurface : struct
 
 class OGLMesh : struct
 {
-   uint vertices;
-   uint normals;
-   uint texCoords;
-   uint texCoords2;
-   uint colors;
+   GLAB vertices;
+   GLAB normals;
+   GLAB texCoords;
+   GLAB texCoords2;
+   GLAB colors;
 };
 
 class OGLIndices : struct
 {
    uint16 * indices;
-   uint buffer;
+   GLEAB buffer;
    uint nIndices;
 };
 
@@ -1343,7 +1643,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
    bool LockSystem(DisplaySystem displaySystem)
    {
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
       OGLSystem oglSystem = displaySystem.driverData;
       if(useSingleGLContext) return true;
    #if defined(__WIN32__)
@@ -1365,7 +1665,7 @@ class OpenGLDisplayDriver : DisplayDriver
       wglMakeCurrent(null, null);
    #elif defined(__unix__) || defined(__APPLE__)
       // printf("Making NULL current\n");
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
       #else
       glXMakeCurrent(xGlobalDisplay, None, null);
       #endif
@@ -1375,7 +1675,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
    bool Lock(Display display)
    {
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
       OGLDisplay oglDisplay = display.driverData;
       if(useSingleGLContext) return true;
    #if defined(__WIN32__)
@@ -1423,7 +1723,7 @@ class OpenGLDisplayDriver : DisplayDriver
          if(oglDisplay.memBitmap) DeleteObject(oglDisplay.memBitmap);
 
    #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
       #else
          if(oglDisplay.shapePixmap)
             XFreePixmap(xGlobalDisplay, oglDisplay.shapePixmap);
@@ -1603,8 +1903,24 @@ class OpenGLDisplayDriver : DisplayDriver
       vboAvailable = true;
       #if defined(__ANDROID__)
          egl_init_display(guiApp.desktop.windowHandle);
+      #elif defined(__ODROID__)
+         egl_init_display((uint)displaySystem.window);
          CheckExtensions(oglSystem);
          result = true;
+      #elif defined(__EMSCRIPTEN__)
+         if(glfwInit() == GL_TRUE)
+         {
+            const int width = 640, height = 480;
+            if(glfwOpenWindow(width, height, 8, 8, 8, 8, 16, 0, GLFW_WINDOW) == GL_TRUE)
+            {
+               //glfwSwapBuffers();
+               result = true;
+            }
+            else
+               printf("glfwOpenWindow() failed\n"); //glfwTerminate();
+         }
+         else
+            printf("glfwInit() failed\n"); //glfwTerminate();
       #else
       {
          X11Window root = RootWindow( xGlobalDisplay, DefaultScreen( xGlobalDisplay ) );
@@ -1667,8 +1983,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__)
+         glfwTerminate();
       #else
       if(oglSystem.visualInfo)
       {
@@ -1693,7 +2011,7 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       bool result = false;
       OGLDisplay oglDisplay = display.driverData;
-#if !defined(__ANDROID__)
+#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
       OGLSystem oglSystem = display.displaySystem.driverData;
 #endif
       if(!oglDisplay)
@@ -1716,7 +2034,7 @@ class OpenGLDisplayDriver : DisplayDriver
          else
             ReleaseDC(display.window, oglDisplay.hdc);
    #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
       #else
          XVisualInfo * visualInfo = ((XWindowData)display.windowDriverData).visual;
          /*
@@ -1775,6 +2093,7 @@ class OpenGLDisplayDriver : DisplayDriver
          glEnable(GL_BLEND);
 
          glMatrixMode(GL_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);
@@ -1782,7 +2101,9 @@ class OpenGLDisplayDriver : DisplayDriver
          glShadeModel(GL_FLAT);
 
          // glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, true);
+#if !defined(EM_MODE)
          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);
@@ -1799,13 +2120,19 @@ class OpenGLDisplayDriver : DisplayDriver
    #if defined(__WIN32__)
          wglMakeCurrent(null, null);
    #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
          result = true;
       #else
          glXMakeCurrent(xGlobalDisplay, None, null);
       #endif
    #endif
       }
+      else
+      {
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
+         result = true;
+      #endif
+      }
 
       return result;
    }
@@ -1976,7 +2303,7 @@ class OpenGLDisplayDriver : DisplayDriver
             ReleaseDC(display.window, hdc);
          }
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
          result = true;
       #else
        int attrib[] =
@@ -2140,7 +2467,7 @@ class OpenGLDisplayDriver : DisplayDriver
 #if defined(__WIN32__)
          wglMakeCurrent(oglDisplay.hdc, oglDisplay.glrc);
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
          width = eglWidth;
          height = eglHeight;
       #else
@@ -2161,6 +2488,7 @@ class OpenGLDisplayDriver : DisplayDriver
       result = false;
 
       glViewport(0,0,width,height);
+      glMatrixMode(GL_PROJECTION);
       glLoadIdentity();
       glOrtho(0,width,height,0,0.0,1.0);
       displayWidth = display.width = width;
@@ -2278,7 +2606,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
             ReleaseDC(0, hdc);
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__ODROID__)
       #else
             XTransform transform =
             {
@@ -2308,8 +2636,10 @@ class OpenGLDisplayDriver : DisplayDriver
          //wglSwapLayerBuffers(oglDisplay.hdc,WGL_SWAP_MAIN_PLANE);
          SwapBuffers(oglDisplay.hdc);
 #elif defined(__unix__) || defined(__APPLE__)
-      #if defined(__ANDROID__)
+      #if defined(__ANDROID__) || defined(__ODROID__)
          eglSwapBuffers(eglDisplay, eglSurface);
+      #elif defined(__EMSCRIPTEN__)
+         glfwSwapBuffers();
       #else
          glXSwapBuffers(xGlobalDisplay, (GLXDrawable)display.window);
       #endif
@@ -2429,8 +2759,9 @@ class OpenGLDisplayDriver : DisplayDriver
          glBindTexture(GL_TEXTURE_2D, 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_MIN_FILTER, mipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
+         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
          //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
          //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
@@ -2438,14 +2769,17 @@ 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);
+         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0 );
 
          glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
 
          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 { };
@@ -2489,6 +2823,8 @@ class OpenGLDisplayDriver : DisplayDriver
          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);
@@ -2692,7 +3028,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
       glColor4fv(oglSurface.foreground);
       glBegin(GL_LINES);
-#ifdef _GLES
+#if defined(_GLES) || defined(EM_MODE)
       if(stippleEnabled)
       {
          glTexCoord2f(0.5f, 0);
@@ -2725,7 +3061,7 @@ class OpenGLDisplayDriver : DisplayDriver
       //Logf("Rectangle\n");
 
       glColor4fv(oglSurface.foreground);
-#ifdef _GLES
+#if defined(_GLES) || defined(EM_MODE)
       if(stippleEnabled)
       {
          glBegin(GL_LINES);
@@ -2774,9 +3110,18 @@ class OpenGLDisplayDriver : DisplayDriver
       //Logf("Area\n");
 
       glColor4fv(oglSurface.background);
+
+#ifdef EM_MODE
+      glBegin(GL_QUADS);
+      glVertex2f(x1+surface.offset.x, y1+surface.offset.y);
+      glVertex2f(x1+surface.offset.x, y2+surface.offset.y+1);
+      glVertex2f(x2+surface.offset.x+1, y2+surface.offset.y+1);
+      glVertex2f(x2+surface.offset.x+1, y1+surface.offset.y);
+      glEnd();
+#else
       glRecti(x1+surface.offset.x, y1+surface.offset.y,
               x2+surface.offset.x + 1, y2+surface.offset.y + 1);
-
+#endif
       /*
       glRectf(x1+surface.offset.x, y1+surface.offset.y,
               x2+surface.offset.x + 1, y2+surface.offset.y + 1);
@@ -2948,6 +3293,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void StretchDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
    {
+#if !defined(EM_MODE)
       float s2dw,s2dh,d2sw,d2sh;
       //bool flipX = false, flipY = false;
 
@@ -3044,10 +3390,12 @@ class OpenGLDisplayDriver : DisplayDriver
          glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
          glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
       }
+#endif
    }
 
    void BlitDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h)
    {
+#if !defined(EM_MODE)
       //Logf("BlitDI\n");
 
       //Clip against the edges of the source
@@ -3108,6 +3456,7 @@ class OpenGLDisplayDriver : DisplayDriver
          glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
          glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
       }
+#endif
    }
 
    void FilterDI(Display display, Surface surface, Bitmap bitmap, int dx, int dy, int sx, int sy, int w, int h, int sw, int sh)
@@ -3208,7 +3557,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
       if(stipple)
       {
-#if defined(_GLES)
+#if defined(_GLES) || defined(EM_MODE)
          stippleEnabled = true;
          glesLineStipple(1, (uint16)stipple);
 #else
@@ -3218,7 +3567,7 @@ class OpenGLDisplayDriver : DisplayDriver
       }
       else
       {
-#if defined(_GLES)
+#if defined(_GLES) || defined(EM_MODE)
          stippleEnabled = false;
          glMatrixMode(GL_TEXTURE);
          glLoadIdentity();
@@ -3269,8 +3618,10 @@ class OpenGLDisplayDriver : DisplayDriver
             break;
          case ambient:
          {
+#if !defined(EM_MODE)
             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:
@@ -3290,6 +3641,7 @@ class OpenGLDisplayDriver : DisplayDriver
 
    void SetLight(Display display, int id, Light light)
    {
+#if !defined(EM_MODE)
       //Logf("SetLight\n");
 
       if(light != null)
@@ -3441,6 +3793,7 @@ class OpenGLDisplayDriver : DisplayDriver
       }
       else
          glDisable(GL_LIGHT0 + id);
+#endif
    }
 
    void SetCamera(Display display, Surface surface, Camera camera)
@@ -3465,10 +3818,10 @@ class OpenGLDisplayDriver : DisplayDriver
          glViewport(x, y, w, h);
 
          // *** Projection Matrix ***
+         glMatrixMode(GL_PROJECTION);
          if(!display.display3D.camera)
             glPushMatrix();
-         else
-            glMatrixMode(GL_PROJECTION);
+
          if(display.display3D.collectingHits)
          {
             float pickX = display.display3D.pickX + surface.offset.x;
@@ -3512,8 +3865,10 @@ class OpenGLDisplayDriver : DisplayDriver
          // ...
 
          glEnable(GL_DEPTH_TEST);
+//#if !defined(EM_MODE)
          glEnable(GL_LIGHTING);
          glShadeModel(GL_SMOOTH);
+//#endif
          glDepthMask((byte)bool::true);
          oglDisplay.depthWrite = true;
 
@@ -3529,7 +3884,9 @@ class OpenGLDisplayDriver : DisplayDriver
          glDisable(GL_LIGHTING);
          glDisable(GL_FOG);
          glDisable(GL_TEXTURE_2D);
+//#if !defined(EM_MODE)
          glShadeModel(GL_FLAT);
+//#endif
          glEnable(GL_BLEND);
          glDisable(GL_MULTISAMPLE_ARB);
 
@@ -3541,7 +3898,6 @@ class OpenGLDisplayDriver : DisplayDriver
          glPopMatrix();
       }
 
-      GLBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
    }
 
    void ApplyMaterial(Display display, Material material, Mesh mesh)
@@ -3551,12 +3907,16 @@ class OpenGLDisplayDriver : DisplayDriver
       // Basic Properties
       if(material.flags.doubleSided)
       {
+#if !defined(EM_MODE)
          glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, !material.flags.singleSideLight);
+#endif
          glDisable(GL_CULL_FACE);
       }
       else
       {
+#if !defined(EM_MODE)
          glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, bool::false);
+#endif
          glEnable(GL_CULL_FACE);
       }
 
@@ -3593,6 +3953,9 @@ class OpenGLDisplayDriver : DisplayDriver
       else
          glDisable(GL_TEXTURE_2D);
 
+#ifdef EM_MODE
+      glColor4f(material.diffuse.r, material.diffuse.g, material.diffuse.b, material.opacity);
+#else
       if(mesh.flags.colors)
       {
          glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
@@ -3620,6 +3983,7 @@ class OpenGLDisplayDriver : DisplayDriver
       }
 
       glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &material.power);
+#endif
    }
 
    void FreeMesh(DisplaySystem displaySystem, Mesh mesh)
@@ -3629,49 +3993,28 @@ class OpenGLDisplayDriver : DisplayDriver
       {
          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.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)
          {
@@ -3689,7 +4032,6 @@ class OpenGLDisplayDriver : DisplayDriver
          mesh.data = OGLMesh { };
       if(mesh.data)
       {
-         OGLMesh oglMesh = mesh.data;
          if(mesh.nVertices == nVertices)
          {
             // Same number of vertices, adding features (Leaves the other features pointers alone)
@@ -3703,8 +4045,6 @@ class OpenGLDisplayDriver : DisplayDriver
                   }
                   else
                      mesh.vertices = new Vector3Df[nVertices];
-                  if(!oglMesh.vertices)
-                     GLGenBuffers(1, &oglMesh.vertices);
                }
                if(!mesh.flags.normals && flags.normals)
                {
@@ -3714,20 +4054,14 @@ class OpenGLDisplayDriver : DisplayDriver
                   }
                   else
                      mesh.normals = new Vector3Df[nVertices];
-                  if(!oglMesh.normals)
-                     GLGenBuffers( 1, &oglMesh.normals);
                }
                if(!mesh.flags.texCoords1 && flags.texCoords1)
                {
                   mesh.texCoords = new Pointf[nVertices];
-                  if(!oglMesh.texCoords)
-                     GLGenBuffers( 1, &oglMesh.texCoords);
                }
                if(!mesh.flags.colors && flags.colors)
                {
                   mesh.colors = new ColorRGBAf[nVertices];
-                  if(!oglMesh.colors)
-                     GLGenBuffers( 1, &oglMesh.colors);
                }
             }
          }
@@ -3743,8 +4077,6 @@ class OpenGLDisplayDriver : DisplayDriver
                }
                else
                   mesh.vertices = renew mesh.vertices Vector3Df[nVertices];
-               if(!oglMesh.vertices)
-                  GLGenBuffers(1, &oglMesh.vertices);
             }
             if(flags.normals)
             {
@@ -3754,20 +4086,14 @@ class OpenGLDisplayDriver : DisplayDriver
                }
                else
                   mesh.normals = renew mesh.normals Vector3Df[nVertices];
-               if(!oglMesh.normals)
-                  GLGenBuffers( 1, &oglMesh.normals);
             }
             if(flags.texCoords1)
             {
                mesh.texCoords = renew mesh.texCoords Pointf[nVertices];
-               if(!oglMesh.texCoords)
-                  GLGenBuffers( 1, &oglMesh.texCoords);
             }
             if(flags.colors)
             {
                mesh.colors = renew mesh.colors ColorRGBAf[nVertices];
-               if(!oglMesh.colors)
-                  GLGenBuffers( 1, &oglMesh.colors);
             }
          }
          result = true;
@@ -3782,31 +4108,21 @@ class OpenGLDisplayDriver : DisplayDriver
 
       if(vboAvailable)
       {
-         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 );
-         }
+         if(flags.vertices)
+            oglMesh.vertices.upload(
+               mesh.nVertices * (mesh.flags.doubleVertices ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.vertices); //, GL_STATIC_DRAW_ARB );
 
-         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.upload(
+               mesh.nVertices * (mesh.flags.doubleNormals ? sizeof(Vector3D) : sizeof(Vector3Df)), mesh.normals); //, GL_STATIC_DRAW_ARB );
 
-         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.upload(
+               mesh.nVertices * sizeof(Pointf), mesh.texCoords); //, GL_STATIC_DRAW_ARB );
 
-         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 );
-         }
-
-         GLBindBuffer( GL_ARRAY_BUFFER_ARB, 0);
+         if(flags.colors)
+            oglMesh.colors.upload(
+               mesh.nVertices * sizeof(ColorRGBAf), mesh.colors); //, GL_STATIC_DRAW_ARB );
       }
    }
 
@@ -3821,8 +4137,7 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       if(oglIndices)
       {
-         if(oglIndices.buffer)
-            GLDeleteBuffers(1, &oglIndices.buffer);
+         oglIndices.buffer.free();
          delete oglIndices.indices;
          delete oglIndices;
       }
@@ -3834,7 +4149,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;
@@ -3844,10 +4158,20 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       if(vboAvailable)
       {
-         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);
+#ifdef _GLES
+         if(indices32bit)
+         {
+            if(!oglIndices.buffer.buffer)
+               GLGenBuffers(1, (GLAB *)&oglIndices.buffer);
+            if(curElementBuffer != oglIndices.buffer.buffer)
+               GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oglIndices.buffer.buffer);
+            glesBufferDatai(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32) * nIndices, oglIndices.indices, GL_STATIC_DRAW_ARB);
+         }
+         else
+#endif
+         oglIndices.buffer.upload(
+            nIndices * (indices32bit ? sizeof(uint32) : sizeof(uint16)),
+            oglIndices.indices); //GL_STATIC_DRAW_ARB);
       }
    }
 
@@ -3861,7 +4185,7 @@ class OpenGLDisplayDriver : DisplayDriver
    {
       //Logf("SelectMesh\n");
 
-#if !defined( __ANDROID__) && !defined(__APPLE__)
+#if !defined( __ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
 
 #if defined(__WIN32__)
       if(glUnlockArraysEXT)
@@ -3878,18 +4202,13 @@ class OpenGLDisplayDriver : DisplayDriver
          glEnableClientState(GL_VERTEX_ARRAY);
          if(!display.display3D.collectingHits && oglMesh)
          {
-            GLBindBuffer(GL_ARRAY_BUFFER_ARB, oglMesh.vertices );
-            if(mesh.flags.doubleVertices)
-               glVertexPointerd(3, 0, (double *)(vboAvailable ? null : mesh.vertices), mesh.nVertices);
-            else
-               glVertexPointer(3, 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 || 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);
+               oglMesh.normals.use(normal, 3, GL_FLOAT, 0, oglMesh.normals.buffer ? null : mesh.normals);
             }
             else
                glDisableClientState(GL_NORMAL_ARRAY);
@@ -3898,8 +4217,7 @@ class OpenGLDisplayDriver : DisplayDriver
             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);
+               oglMesh.texCoords.use(texCoord, 2, GL_FLOAT, 0, oglMesh.texCoords.buffer ? null : mesh.texCoords);
             }
             else
                glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -3908,44 +4226,38 @@ class OpenGLDisplayDriver : DisplayDriver
             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);
+               oglMesh.colors.use(color, 4, GL_FLOAT, 0, oglMesh.colors.buffer ? null : mesh.colors);
             }
             else
                glDisableClientState(GL_COLOR_ARRAY);
-
          }
          else
          {
-            GLBindBuffer( GL_ARRAY_BUFFER_ARB, 0);
-            if(mesh.flags.doubleVertices)
-               glVertexPointerd(3, 0, (double *)mesh.vertices, mesh.nVertices);
-            else
-               glVertexPointer(3, GL_FLOAT, 0, mesh.vertices);
+            noAB.use(vertex, 3, (mesh.flags.doubleVertices ? GL_DOUBLE : GL_FLOAT), 0, (double *)mesh.vertices);
             if((mesh.normals || mesh.flags.normals) && !display.display3D.collectingHits)
             {
                glEnableClientState(GL_NORMAL_ARRAY);
-               glNormalPointer(/*mesh.flags.doubleNormals ? GL_DOUBLE : */GL_FLOAT, 0, mesh.normals);
+               noAB.use(normal, 3, GL_FLOAT, 0, mesh.normals);
             }
             else
                glDisableClientState(GL_NORMAL_ARRAY);
             if((mesh.texCoords || mesh.flags.texCoords1) && !display.display3D.collectingHits)
             {
                glEnableClientState(GL_TEXTURE_COORD_ARRAY);
-               glTexCoordPointer(2, GL_FLOAT, 0, mesh.texCoords);
+               noAB.use(texCoord, 2, GL_FLOAT, 0, mesh.texCoords);
             }
             else
                glDisableClientState(GL_TEXTURE_COORD_ARRAY);
             if((mesh.colors || mesh.flags.colors) && !display.display3D.collectingHits)
             {
                glEnableClientState(GL_COLOR_ARRAY);
-               glColorPointer(4, GL_FLOAT, 0, mesh.colors);
+               noAB.use(color, 4, GL_FLOAT, 0, mesh.colors);
             }
             else
                glDisableClientState(GL_COLOR_ARRAY);
          }
 
-#if !defined(__ANDROID__) && !defined(__APPLE__)
+#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__)
 
 #if defined(__WIN32__)
          if(glLockArraysEXT)
@@ -3955,8 +4267,6 @@ class OpenGLDisplayDriver : DisplayDriver
 
 #endif
       }
-      else
-         GLBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
    }
 
    void DrawPrimitives(Display display, PrimitiveSingle * primitive, Mesh mesh)
@@ -3992,27 +4302,15 @@ class OpenGLDisplayDriver : DisplayDriver
          }
          else*/
 #endif
+
          {
             OGLIndices oglIndices = primitive->data;
+            GLEAB eab = ((!display.display3D.collectingHits && oglIndices) ? oglIndices.buffer : noEAB);
 
-            if(!display.display3D.collectingHits && vboAvailable && oglIndices)
-            {
-               GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, oglIndices.buffer);
-               if(primitive->type.indices32bit)
-                  glDrawElementsi(primitiveTypes[primitive->type.primitiveType], primitive->nIndices, 0);
-               else
-                  glDrawElements(primitiveTypes[primitive->type.primitiveType], primitive->nIndices, GL_UNSIGNED_SHORT, 0);
-               GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
-            }
-            else
-            {
-               if(primitive->type.indices32bit)
-                  glDrawElementsi(primitiveTypes[primitive->type.primitiveType], primitive->nIndices,
-                     oglIndices ? oglIndices.indices : primitive->indices);
-               else
-                  glDrawElements(primitiveTypes[primitive->type.primitiveType], primitive->nIndices,
-                     GL_UNSIGNED_SHORT, oglIndices ? oglIndices.indices : primitive->indices);
-            }
+            eab.draw(primitiveTypes[primitive->type.primitiveType], primitive->nIndices,
+               primitive->type.indices32bit ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT,
+               eab.buffer ? 0 : (oglIndices ? oglIndices.indices : primitive->indices));
+            GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
          }
       }
    }
@@ -4075,11 +4373,12 @@ 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__)
+#else
       OGLSystem system = displaySystem.driverData;
       return system.glContext;
-#else
-      return eglContext;
 #endif
    }
    return null;