From e05655679e7bd9488c31169ece3d404fd9160c8d Mon Sep 17 00:00:00 2001
From: Andrei Karas <akaras@inbox.ru>
Date: Sun, 8 Jun 2014 01:36:38 +0300
Subject: copy mobileopengl into modernopengl.

---
 src/CMakeLists.txt                  |    2 +
 src/Makefile.am                     |    2 +
 src/graphicsmanager.cpp             |    8 +-
 src/render/modernopenglgraphics.cpp | 1362 +++++++++++++++++++++++++++++++++++
 src/render/modernopenglgraphics.h   |   98 +++
 src/resources/image.h               |    1 +
 src/resources/openglimagehelper.cpp |    5 +-
 7 files changed, 1476 insertions(+), 2 deletions(-)
 create mode 100644 src/render/modernopenglgraphics.cpp
 create mode 100644 src/render/modernopenglgraphics.h

(limited to 'src')

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8e5fd17af..697b7bfe4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -851,6 +851,8 @@ SET(SRCS
     render/mgltypes.h
     render/mobileopenglgraphics.cpp
     render/mobileopenglgraphics.h
+    render/modernopenglgraphics.cpp
+    render/modernopenglgraphics.h
     input/mouseinput.h
     mumblemanager.cpp
     mumblemanager.h
diff --git a/src/Makefile.am b/src/Makefile.am
index ca904f168..27d501638 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -970,6 +970,8 @@ manaplus_SOURCES += gui/widgets/avatarlistbox.cpp \
 	      render/mgltypes.h \
 	      render/mobileopenglgraphics.cpp \
 	      render/mobileopenglgraphics.h \
+	      render/modernopenglgraphics.cpp \
+	      render/modernopenglgraphics.h \
 	      input/mouseinput.h \
 	      navigationmanager.cpp \
 	      navigationmanager.h \
diff --git a/src/graphicsmanager.cpp b/src/graphicsmanager.cpp
index 33cbd0224..299255abe 100644
--- a/src/graphicsmanager.cpp
+++ b/src/graphicsmanager.cpp
@@ -45,6 +45,7 @@
 #include "render/graphics.h"
 #include "render/mgl.h"
 #include "render/mobileopenglgraphics.h"
+#include "render/modernopenglgraphics.h"
 #include "render/normalopenglgraphics.h"
 #include "render/renderers.h"
 #include "render/safeopenglgraphics.h"
@@ -272,7 +273,6 @@ void GraphicsManager::createRenderers()
             mUseTextureSampler = false;
             break;
         case RENDER_NORMAL_OPENGL:
-        case RENDER_MODERN_OPENGL:
 #ifndef USE_SDL2
         case RENDER_SDL2_DEFAULT:
 #endif
@@ -291,6 +291,12 @@ void GraphicsManager::createRenderers()
             mainGraphics = new SafeOpenGLGraphics;
             mUseTextureSampler = false;
             break;
+        case RENDER_MODERN_OPENGL:
+            imageHelper = new OpenGLImageHelper;
+            surfaceImageHelper = new SurfaceImageHelper;
+            mainGraphics = new ModernOpenGLGraphics;
+            mUseTextureSampler = true;
+            break;
 #endif
         case RENDER_GLES_OPENGL:
             imageHelper = new OpenGLImageHelper;
diff --git a/src/render/modernopenglgraphics.cpp b/src/render/modernopenglgraphics.cpp
new file mode 100644
index 000000000..553a864e3
--- /dev/null
+++ b/src/render/modernopenglgraphics.cpp
@@ -0,0 +1,1362 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2014  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "main.h"
+
+#ifdef USE_OPENGL
+
+#include "render/modernopenglgraphics.h"
+
+#include "configuration.h"
+#include "graphicsmanager.h"
+#include "graphicsvertexes.h"
+#include "logger.h"
+
+#include "render/mgl.h"
+
+#include "resources/image.h"
+#include "resources/imagerect.h"
+#include "resources/openglimagehelper.h"
+
+#include "utils/sdlcheckutils.h"
+
+#include "debug.h"
+
+#define vertFill2D(tVar, vVar, x1, y1, x2, y2, dstX, dstY, w, h) \
+    tVar[vp + 0] = x1; \
+    tVar[vp + 1] = y1; \
+    tVar[vp + 2] = x2; \
+    tVar[vp + 3] = y1; \
+    tVar[vp + 4] = x2; \
+    tVar[vp + 5] = y2; \
+    tVar[vp + 6] = x1; \
+    tVar[vp + 7] = y1; \
+    tVar[vp + 8] = x1; \
+    tVar[vp + 9] = y2; \
+    tVar[vp + 10] = x2; \
+    tVar[vp + 11] = y2; \
+    vVar[vp + 0] = static_cast<GLshort>(dstX); \
+    vVar[vp + 1] = static_cast<GLshort>(dstY); \
+    vVar[vp + 2] = static_cast<GLshort>(dstX + w); \
+    vVar[vp + 3] = static_cast<GLshort>(dstY); \
+    vVar[vp + 4] = static_cast<GLshort>(dstX + w); \
+    vVar[vp + 5] = static_cast<GLshort>(dstY + h); \
+    vVar[vp + 6] = static_cast<GLshort>(dstX); \
+    vVar[vp + 7] = static_cast<GLshort>(dstY); \
+    vVar[vp + 8] = static_cast<GLshort>(dstX); \
+    vVar[vp + 9] = static_cast<GLshort>(dstY + h); \
+    vVar[vp + 10] = static_cast<GLshort>(dstX + w); \
+    vVar[vp + 11] = static_cast<GLshort>(dstY + h);
+
+GLuint ModernOpenGLGraphics::mLastImage = 0;
+#ifdef DEBUG_DRAW_CALLS
+unsigned int ModernOpenGLGraphics::mDrawCalls = 0;
+unsigned int ModernOpenGLGraphics::mLastDrawCalls = 0;
+#endif
+
+ModernOpenGLGraphics::ModernOpenGLGraphics() :
+    mFloatTexArray(nullptr),
+    mShortVertArray(nullptr),
+    mFloatTexArrayCached(nullptr),
+    mShortVertArrayCached(nullptr),
+    mAlphaCached(1.0F),
+    mVpCached(0),
+    mTexture(false),
+    mIsByteColor(false),
+    mByteColor(),
+    mImageCached(0),
+    mFloatColor(1.0F),
+    mMaxVertices(500),
+    mColorAlpha(false),
+#ifdef DEBUG_BIND_TEXTURE
+    mOldTexture(),
+    mOldTextureId(0),
+#endif
+    mFbo()
+{
+    mOpenGL = RENDER_MODERN_OPENGL;
+    mName = "modern OpenGL";
+}
+
+ModernOpenGLGraphics::~ModernOpenGLGraphics()
+{
+    deleteArraysInternal();
+}
+
+void ModernOpenGLGraphics::initArrays(const int vertCount)
+{
+    mMaxVertices = vertCount;
+    if (mMaxVertices < 500)
+        mMaxVertices = 500;
+    else if (mMaxVertices > 1024)
+        mMaxVertices = 1024;
+
+    // need alocate small size, after if limit reached reallocate to double size
+    const size_t sz = mMaxVertices * 4 + 30;
+    vertexBufSize = mMaxVertices;
+    if (!mFloatTexArray)
+        mFloatTexArray = new GLfloat[sz];
+    if (!mShortVertArray)
+        mShortVertArray = new GLshort[sz];
+    if (!mFloatTexArrayCached)
+        mFloatTexArrayCached = new GLfloat[sz];
+    if (!mShortVertArrayCached)
+        mShortVertArrayCached = new GLshort[sz];
+}
+
+void ModernOpenGLGraphics::deleteArrays()
+{
+    deleteArraysInternal();
+}
+
+void ModernOpenGLGraphics::deleteArraysInternal()
+{
+    delete [] mFloatTexArray;
+    mFloatTexArray = nullptr;
+    delete [] mShortVertArray;
+    mShortVertArray = nullptr;
+    delete [] mFloatTexArrayCached;
+    mFloatTexArrayCached = nullptr;
+    delete [] mShortVertArrayCached;
+    mShortVertArrayCached = nullptr;
+}
+
+bool ModernOpenGLGraphics::setVideoMode(const int w, const int h,
+                                        const int scale,
+                                        const int bpp,
+                                        const bool fs,
+                                        const bool hwaccel,
+                                        const bool resize,
+                                        const bool noFrame)
+{
+    setMainFlags(w, h, scale, bpp, fs, hwaccel, resize, noFrame);
+
+    return setOpenGLMode();
+}
+
+static inline void drawQuad(const Image *const image,
+                            const int srcX, const int srcY,
+                            const int dstX, const int dstY,
+                            const int width, const int height)
+{
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+    {
+        const float tw = static_cast<float>(image->mTexWidth);
+        const float th = static_cast<float>(image->mTexHeight);
+        // Find OpenGL normalized texture coordinates.
+        const float texX1 = static_cast<float>(srcX) / tw;
+        const float texY1 = static_cast<float>(srcY) / th;
+        const float texX2 = static_cast<float>(srcX + width) / tw;
+        const float texY2 = static_cast<float>(srcY + height) / th;
+
+        GLfloat tex[] =
+        {
+            texX1, texY1,
+            texX2, texY1,
+            texX1, texY2,
+            texX2, texY2
+        };
+
+        GLshort vert[] =
+        {
+            static_cast<GLshort>(dstX), static_cast<GLshort>(dstY),
+            static_cast<GLshort>(dstX + width), static_cast<GLshort>(dstY),
+            static_cast<GLshort>(dstX), static_cast<GLshort>(dstY + height),
+            static_cast<GLshort>(dstX + width),
+                static_cast<GLshort>(dstY + height)
+        };
+
+        glVertexPointer(2, GL_SHORT, 0, &vert);
+        glTexCoordPointer(2, GL_FLOAT, 0, &tex);
+
+#ifdef DEBUG_DRAW_CALLS
+        ModernOpenGLGraphics::mDrawCalls ++;
+#endif
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
+}
+
+static inline void drawRescaledQuad(const Image *const image,
+                                    const int srcX, const int srcY,
+                                    const int dstX, const int dstY,
+                                    const int width, const int height,
+                                    const int desiredWidth,
+                                    const int desiredHeight)
+{
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+    {
+        const float tw = static_cast<float>(image->mTexWidth);
+        const float th = static_cast<float>(image->mTexHeight);
+        // Find OpenGL normalized texture coordinates.
+        const float texX1 = static_cast<float>(srcX) / tw;
+        const float texY1 = static_cast<float>(srcY) / th;
+        const float texX2 = static_cast<float>(srcX + width) / tw;
+        const float texY2 = static_cast<float>(srcY + height) / th;
+
+        GLfloat tex[] =
+        {
+            texX1, texY1,
+            texX2, texY1,
+            texX1, texY2,
+            texX2, texY2
+        };
+
+        GLshort vert[] =
+        {
+            static_cast<GLshort>(dstX), static_cast<GLshort>(dstY),
+            static_cast<GLshort>(dstX + desiredWidth),
+                static_cast<GLshort>(dstY),
+            static_cast<GLshort>(dstX), static_cast<GLshort>(
+                dstY + desiredHeight),
+            static_cast<GLshort>(dstX + desiredWidth),
+                static_cast<GLshort>(dstY + desiredHeight)
+        };
+        glVertexPointer(2, GL_SHORT, 0, &vert);
+        glTexCoordPointer(2, GL_FLOAT, 0, &tex);
+
+#ifdef DEBUG_DRAW_CALLS
+        ModernOpenGLGraphics::mDrawCalls ++;
+#endif
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
+}
+
+bool ModernOpenGLGraphics::drawImage(const Image *const image,
+                                     int dstX, int dstY)
+{
+    return drawImageInline(image, dstX, dstY);
+}
+
+bool ModernOpenGLGraphics::drawImageInline(const Image *const image,
+                                           int dstX, int dstY)
+{
+    FUNC_BLOCK("Graphics::drawImage", 1)
+    if (!image)
+        return false;
+
+    setColorAlpha(image->mAlpha);
+#ifdef DEBUG_BIND_TEXTURE
+    debugBindTexture(image);
+#endif
+    bindTexture(OpenGLImageHelper::mTextureType, image->mGLImage);
+    setTexturingAndBlending(true);
+
+    const SDL_Rect &imageRect = image->mBounds;
+    drawQuad(image, imageRect.x, imageRect.y,
+        dstX, dstY, imageRect.w, imageRect.h);
+
+    return true;
+}
+
+void ModernOpenGLGraphics::drawImageCached(const Image *const image,
+                                           int x, int y)
+{
+    if (!image)
+        return;
+
+    if (image->mGLImage != mImageCached)
+    {
+        completeCache();
+        mImageCached = image->mGLImage;
+        mAlphaCached = image->mAlpha;
+    }
+
+    const SDL_Rect &imageRect = image->mBounds;
+    const int srcX = imageRect.x;
+    const int srcY = imageRect.y;
+    const int w = imageRect.w;
+    const int h = imageRect.h;
+
+    if (w == 0 || h == 0)
+        return;
+
+    const float tw = static_cast<float>(image->mTexWidth);
+    const float th = static_cast<float>(image->mTexHeight);
+
+    const unsigned int vLimit = mMaxVertices * 4;
+
+    unsigned int vp = mVpCached;
+
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+    {
+        float texX1 = static_cast<float>(srcX) / tw;
+        float texY1 = static_cast<float>(srcY) / th;
+        float texX2 = static_cast<float>(srcX + w) / tw;
+        float texY2 = static_cast<float>(srcY + h) / th;
+
+        vertFill2D(mFloatTexArrayCached, mShortVertArrayCached,
+            texX1, texY1, texX2, texY2,
+            x, y, w, h);
+
+        vp += 12;
+        if (vp >= vLimit)
+        {
+            completeCache();
+            vp = 0;
+        }
+        else
+        {
+            mVpCached = vp;
+        }
+    }
+}
+
+void ModernOpenGLGraphics::drawPatternCached(const Image *const image,
+                                             const int x, const int y,
+                                             const int w, const int h)
+{
+    if (!image)
+        return;
+
+    if (image->mGLImage != mImageCached)
+    {
+        completeCache();
+        mImageCached = image->mGLImage;
+    }
+
+    const SDL_Rect &imageRect = image->mBounds;
+    const int srcX = imageRect.x;
+    const int srcY = imageRect.y;
+    const int iw = imageRect.w;
+    const int ih = imageRect.h;
+
+    if (iw == 0 || ih == 0)
+        return;
+
+    const float tw = static_cast<float>(image->mTexWidth);
+    const float th = static_cast<float>(image->mTexHeight);
+
+    unsigned int vp = mVpCached;
+    const unsigned int vLimit = mMaxVertices * 4;
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+//    {
+        const float texX1 = static_cast<float>(srcX) / tw;
+        const float texY1 = static_cast<float>(srcY) / th;
+
+        for (int py = 0; py < h; py += ih)
+        {
+            const int height = (py + ih >= h) ? h - py : ih;
+            const float texY2 = static_cast<float>(srcY + height) / th;
+            const int dstY = y + py;
+            for (int px = 0; px < w; px += iw)
+            {
+                const int width = (px + iw >= w) ? w - px : iw;
+                const int dstX = x + px;
+
+                const float texX2 = static_cast<float>(srcX + width) / tw;
+
+                vertFill2D(mFloatTexArrayCached, mShortVertArrayCached,
+                    texX1, texY1, texX2, texY2,
+                    dstX, dstY, width, height);
+
+                vp += 12;
+                if (vp >= vLimit)
+                {
+                    completeCache();
+                    vp = 0;
+                }
+            }
+        }
+//    }
+    mVpCached = vp;
+}
+
+void ModernOpenGLGraphics::completeCache()
+{
+    if (!mImageCached)
+        return;
+
+    setColorAlpha(mAlphaCached);
+#ifdef DEBUG_BIND_TEXTURE
+//    debugBindTexture(image);
+#endif
+    bindTexture(OpenGLImageHelper::mTextureType, mImageCached);
+    setTexturingAndBlending(true);
+
+    drawTriangleArrayfsCached(mVpCached);
+    mImageCached = 0;
+    mVpCached = 0;
+}
+
+bool ModernOpenGLGraphics::drawRescaledImage(const Image *const image,
+                                             int dstX, int dstY,
+                                             const int desiredWidth,
+                                             const int desiredHeight)
+{
+    FUNC_BLOCK("Graphics::drawRescaledImage", 1)
+    if (!image)
+        return false;
+
+    const SDL_Rect &imageRect = image->mBounds;
+
+    // Just draw the image normally when no resizing is necessary,
+    if (imageRect.w == desiredWidth && imageRect.h == desiredHeight)
+        return drawImageInline(image, dstX, dstY);
+
+    setColorAlpha(image->mAlpha);
+#ifdef DEBUG_BIND_TEXTURE
+    debugBindTexture(image);
+#endif
+    bindTexture(OpenGLImageHelper::mTextureType, image->mGLImage);
+    setTexturingAndBlending(true);
+
+    // Draw a textured quad.
+    drawRescaledQuad(image, imageRect.x, imageRect.y, dstX, dstY,
+        imageRect.w, imageRect.h, desiredWidth, desiredHeight);
+
+    return true;
+}
+
+void ModernOpenGLGraphics::drawPattern(const Image *const image,
+                                       const int x, const int y,
+                                       const int w, const int h)
+{
+    drawPatternInline(image, x, y, w, h);
+}
+
+void ModernOpenGLGraphics::drawPatternInline(const Image *const image,
+                                             const int x, const int y,
+                                             const int w, const int h)
+{
+    FUNC_BLOCK("Graphics::drawPattern", 1)
+    if (!image)
+        return;
+
+    const SDL_Rect &imageRect = image->mBounds;
+    const int srcX = imageRect.x;
+    const int srcY = imageRect.y;
+    const int iw = imageRect.w;
+    const int ih = imageRect.h;
+
+    if (iw == 0 || ih == 0)
+        return;
+
+    const float tw = static_cast<float>(image->mTexWidth);
+    const float th = static_cast<float>(image->mTexHeight);
+
+    setColorAlpha(image->mAlpha);
+
+#ifdef DEBUG_BIND_TEXTURE
+    debugBindTexture(image);
+#endif
+    bindTexture(OpenGLImageHelper::mTextureType, image->mGLImage);
+
+    setTexturingAndBlending(true);
+
+    unsigned int vp = 0;
+    const unsigned int vLimit = mMaxVertices * 4;
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+//    {
+        const float texX1 = static_cast<float>(srcX) / tw;
+        const float texY1 = static_cast<float>(srcY) / th;
+
+        for (int py = 0; py < h; py += ih)
+        {
+            const int height = (py + ih >= h) ? h - py : ih;
+            const float texY2 = static_cast<float>(srcY + height) / th;
+            const int dstY = y + py;
+            for (int px = 0; px < w; px += iw)
+            {
+                const int width = (px + iw >= w) ? w - px : iw;
+                const int dstX = x + px;
+
+                const float texX2 = static_cast<float>(srcX + width) / tw;
+
+                vertFill2D(mFloatTexArray, mShortVertArray,
+                    texX1, texY1, texX2, texY2,
+                    dstX, dstY, width, height);
+
+                vp += 12;
+                if (vp >= vLimit)
+                {
+                    drawTriangleArrayfs(vp);
+                    vp = 0;
+                }
+            }
+        }
+        if (vp > 0)
+            drawTriangleArrayfs(vp);
+//    }
+}
+
+void ModernOpenGLGraphics::drawRescaledPattern(const Image *const image,
+                                               const int x, const int y,
+                                               const int w, const int h,
+                                               const int scaledWidth,
+                                               const int scaledHeight)
+{
+    if (!image)
+        return;
+
+    if (scaledWidth == 0 || scaledHeight == 0)
+        return;
+
+    const SDL_Rect &imageRect = image->mBounds;
+    const int srcX = imageRect.x;
+    const int srcY = imageRect.y;
+    const int iw = imageRect.w;
+    const int ih = imageRect.h;
+    if (iw == 0 || ih == 0)
+        return;
+
+    setColorAlpha(image->mAlpha);
+
+#ifdef DEBUG_BIND_TEXTURE
+    debugBindTexture(image);
+#endif
+    bindTexture(OpenGLImageHelper::mTextureType, image->mGLImage);
+
+    setTexturingAndBlending(true);
+
+    unsigned int vp = 0;
+    const unsigned int vLimit = mMaxVertices * 4;
+
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+//    {
+        const float tw = static_cast<float>(image->mTexWidth);
+        const float th = static_cast<float>(image->mTexHeight);
+
+        const float texX1 = static_cast<float>(srcX) / tw;
+        const float texY1 = static_cast<float>(srcY) / th;
+
+        const float tFractionW = iw / tw;
+        const float tFractionH = ih / th;
+
+        for (int py = 0; py < h; py += scaledHeight)
+        {
+            const int height = (py + scaledHeight >= h)
+                ? h - py : scaledHeight;
+            const int dstY = y + py;
+            const float visibleFractionH = static_cast<float>(height)
+                / scaledHeight;
+            const float texY2 = texY1 + tFractionH * visibleFractionH;
+            for (int px = 0; px < w; px += scaledWidth)
+            {
+                const int width = (px + scaledWidth >= w)
+                    ? w - px : scaledWidth;
+                const int dstX = x + px;
+                const float visibleFractionW = static_cast<float>(width)
+                    / scaledWidth;
+                const float texX2 = texX1 + tFractionW * visibleFractionW;
+
+                vertFill2D(mFloatTexArray, mShortVertArray,
+                    texX1, texY1, texX2, texY2,
+                    dstX, dstY, width, height);
+
+                vp += 12;
+                if (vp >= vLimit)
+                {
+                    drawTriangleArrayfs(vp);
+                    vp = 0;
+                }
+            }
+        }
+        if (vp > 0)
+            drawTriangleArrayfs(vp);
+//    }
+}
+
+inline void ModernOpenGLGraphics::drawVertexes(const
+                                               NormalOpenGLGraphicsVertexes
+                                               &ogl)
+{
+    const std::vector<GLshort*> &shortVertPool = ogl.mShortVertPool;
+    std::vector<GLshort*>::const_iterator iv;
+    const std::vector<GLshort*>::const_iterator iv_end = shortVertPool.end();
+    const std::vector<int> &vp = ogl.mVp;
+    std::vector<int>::const_iterator ivp;
+    const std::vector<int>::const_iterator ivp_end = vp.end();
+
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+    {
+        const std::vector<GLfloat*> &floatTexPool = ogl.mFloatTexPool;
+        std::vector<GLfloat*>::const_iterator ft;
+        const std::vector<GLfloat*>::const_iterator
+            ft_end = floatTexPool.end();
+
+        for (iv = shortVertPool.begin(), ft = floatTexPool.begin(),
+             ivp = vp.begin();
+             iv != iv_end && ft != ft_end && ivp != ivp_end;
+             ++ iv, ++ ft, ++ ivp)
+        {
+            drawTriangleArrayfs(*iv, *ft, *ivp);
+        }
+    }
+}
+
+void ModernOpenGLGraphics::calcPattern(ImageVertexes *const vert,
+                                       const Image *const image,
+                                       const int x, const int y,
+                                       const int w, const int h) const
+{
+    calcPatternInline(vert, image, x, y, w, h);
+}
+
+void ModernOpenGLGraphics::calcPatternInline(ImageVertexes *const vert,
+                                             const Image *const image,
+                                             const int x, const int y,
+                                             const int w, const int h) const
+{
+    if (!image || !vert)
+        return;
+
+    const SDL_Rect &imageRect = image->mBounds;
+    const int srcX = imageRect.x;
+    const int srcY = imageRect.y;
+    const int iw = imageRect.w;
+    const int ih = imageRect.h;
+
+    if (iw == 0 || ih == 0)
+        return;
+
+    const float tw = static_cast<float>(image->mTexWidth);
+    const float th = static_cast<float>(image->mTexHeight);
+
+    const unsigned int vLimit = mMaxVertices * 4;
+
+    NormalOpenGLGraphicsVertexes &ogl = vert->ogl;
+    unsigned int vp = ogl.continueVp();
+
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+    {
+        const float texX1 = static_cast<float>(srcX) / tw;
+        const float texY1 = static_cast<float>(srcY) / th;
+
+        GLfloat *floatTexArray = ogl.continueFloatTexArray();
+        GLshort *shortVertArray = ogl.continueShortVertArray();
+
+        for (int py = 0; py < h; py += ih)
+        {
+            const int height = (py + ih >= h) ? h - py : ih;
+            const int dstY = y + py;
+            const float texY2 = static_cast<float>(srcY + height) / th;
+            for (int px = 0; px < w; px += iw)
+            {
+                const int width = (px + iw >= w) ? w - px : iw;
+                const int dstX = x + px;
+                const float texX2 = static_cast<float>(srcX + width) / tw;
+
+                vertFill2D(floatTexArray, shortVertArray,
+                    texX1, texY1, texX2, texY2,
+                    dstX, dstY, width, height);
+
+                vp += 12;
+                if (vp >= vLimit)
+                {
+                    floatTexArray = ogl.switchFloatTexArray();
+                    shortVertArray = ogl.switchShortVertArray();
+                    ogl.switchVp(vp);
+                    vp = 0;
+                }
+            }
+        }
+    }
+    ogl.switchVp(vp);
+}
+
+void ModernOpenGLGraphics::calcTileCollection(ImageCollection *const vertCol,
+                                              const Image *const image,
+                                              int x, int y)
+{
+    if (vertCol->currentGLImage != image->mGLImage)
+    {
+        ImageVertexes *const vert = new ImageVertexes();
+        vertCol->currentGLImage = image->mGLImage;
+        vertCol->currentVert = vert;
+        vert->image = image;
+        vertCol->draws.push_back(vert);
+        calcTileVertexesInline(vert, image, x, y);
+    }
+    else
+    {
+        calcTileVertexesInline(vertCol->currentVert, image, x, y);
+    }
+}
+
+void ModernOpenGLGraphics::drawTileCollection(const ImageCollection
+                                              *const vertCol)
+{
+    const ImageVertexesVector &draws = vertCol->draws;
+    const ImageCollectionCIter it_end = draws.end();
+    for (ImageCollectionCIter it = draws.begin(); it != it_end; ++ it)
+    {
+        const ImageVertexes *const vert = *it;
+        const Image *const image = vert->image;
+
+        setColorAlpha(image->mAlpha);
+#ifdef DEBUG_BIND_TEXTURE
+        debugBindTexture(image);
+#endif
+        bindTexture(OpenGLImageHelper::mTextureType, image->mGLImage);
+        setTexturingAndBlending(true);
+        drawVertexes(vert->ogl);
+    }
+}
+
+void ModernOpenGLGraphics::calcPattern(ImageCollection* const vertCol,
+                                       const Image *const image,
+                                       const int x, const int y,
+                                       const int w, const int h) const
+{
+    ImageVertexes *vert = nullptr;
+    if (vertCol->currentGLImage != image->mGLImage)
+    {
+        vert = new ImageVertexes();
+        vertCol->currentGLImage = image->mGLImage;
+        vertCol->currentVert = vert;
+        vert->image = image;
+        vertCol->draws.push_back(vert);
+    }
+    else
+    {
+        vert = vertCol->currentVert;
+    }
+
+    calcPatternInline(vert, image, x, y, w, h);
+}
+
+void ModernOpenGLGraphics::calcTileVertexes(ImageVertexes *const vert,
+                                            const Image *const image,
+                                            int dstX, int dstY) const
+{
+    calcTileVertexesInline(vert, image, dstX, dstY);
+}
+
+void ModernOpenGLGraphics::calcTileVertexesInline(ImageVertexes *const vert,
+                                                  const Image *const image,
+                                                  int dstX, int dstY) const
+{
+    if (!vert || !image)
+        return;
+
+    const SDL_Rect &imageRect = image->mBounds;
+    const int srcX = imageRect.x;
+    const int srcY = imageRect.y;
+    const int w = imageRect.w;
+    const int h = imageRect.h;
+
+    if (w == 0 || h == 0)
+        return;
+
+    const float tw = static_cast<float>(image->mTexWidth);
+    const float th = static_cast<float>(image->mTexHeight);
+
+    const unsigned int vLimit = mMaxVertices * 4;
+
+    NormalOpenGLGraphicsVertexes &ogl = vert->ogl;
+
+//    std::vector<int> *vps = ogl.getVp();
+    unsigned int vp = ogl.continueVp();
+
+    // Draw a set of textured rectangles
+//    if (OpenGLImageHelper::mTextureType == GL_TEXTURE_2D)
+    {
+        float texX1 = static_cast<float>(srcX) / tw;
+        float texY1 = static_cast<float>(srcY) / th;
+        float texX2 = static_cast<float>(srcX + w) / tw;
+        float texY2 = static_cast<float>(srcY + h) / th;
+
+        GLfloat *const floatTexArray = ogl.continueFloatTexArray();
+        GLshort *const shortVertArray = ogl.continueShortVertArray();
+
+        vertFill2D(floatTexArray, shortVertArray,
+            texX1, texY1, texX2, texY2,
+            dstX, dstY, w, h);
+
+        vp += 12;
+        if (vp >= vLimit)
+        {
+            ogl.switchFloatTexArray();
+            ogl.switchShortVertArray();
+            ogl.switchVp(vp);
+            vp = 0;
+        }
+    }
+    ogl.switchVp(vp);
+}
+
+void ModernOpenGLGraphics::drawTileVertexes(const ImageVertexes *const vert)
+{
+    if (!vert)
+        return;
+    const Image *const image = vert->image;
+
+    setColorAlpha(image->mAlpha);
+#ifdef DEBUG_BIND_TEXTURE
+    debugBindTexture(image);
+#endif
+    bindTexture(OpenGLImageHelper::mTextureType, image->mGLImage);
+    setTexturingAndBlending(true);
+    drawVertexes(vert->ogl);
+}
+
+void ModernOpenGLGraphics::calcWindow(ImageCollection *const vertCol,
+                                      const int x, const int y,
+                                      const int w, const int h,
+                                      const ImageRect &imgRect)
+{
+    ImageVertexes *vert = nullptr;
+    const Image *const image = imgRect.grid[4];
+    if (vertCol->currentGLImage != image->mGLImage)
+    {
+        vert = new ImageVertexes();
+        vertCol->currentGLImage = image->mGLImage;
+        vertCol->currentVert = vert;
+        vert->image = image;
+        vertCol->draws.push_back(vert);
+    }
+    else
+    {
+        vert = vertCol->currentVert;
+    }
+    calcImageRect(vert, x, y, w, h, imgRect);
+}
+
+void ModernOpenGLGraphics::updateScreen()
+{
+    BLOCK_START("Graphics::updateScreen")
+//    glFlush();
+//    glFinish();
+#ifdef DEBUG_DRAW_CALLS
+    mLastDrawCalls = mDrawCalls;
+    mDrawCalls = 0;
+#endif
+#ifdef USE_SDL2
+    SDL_GL_SwapWindow(mWindow);
+#else
+    SDL_GL_SwapBuffers();
+#endif
+#ifdef DEBUG_OPENGL
+    if (mglFrameTerminator)
+        mglFrameTerminator();
+#endif
+// may be need clear?
+//  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    BLOCK_END("Graphics::updateScreen")
+}
+
+void ModernOpenGLGraphics::beginDraw()
+{
+    glMatrixMode(GL_TEXTURE);
+    glLoadIdentity();
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+#ifdef ANDROID
+    glOrthof(0.0, static_cast<float>(mRect.w),
+        static_cast<float>(mRect.h),
+        0.0, -1.0, 1.0);
+#else
+    glOrtho(0.0, static_cast<double>(mRect.w),
+        static_cast<double>(mRect.h),
+        0.0, -1.0, 1.0);
+#endif
+
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+
+    glEnable(GL_SCISSOR_TEST);
+    glDisable(GL_DITHER);
+    glDisable(GL_LIGHTING);
+    glDisable(GL_DEPTH_TEST);
+    glDisable(GL_FOG);
+    glDisable(GL_COLOR_LOGIC_OP);
+    glDisable(GL_COLOR_MATERIAL);
+    glDisable(GL_STENCIL_TEST);
+
+    glShadeModel(GL_FLAT);
+
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    glEnableClientState(GL_VERTEX_ARRAY);
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+#ifndef ANDROID
+    glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
+    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+    glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
+    glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
+#ifndef __MINGW32__
+    glHint(GL_TEXTURE_COMPRESSION_HINT, GL_FASTEST);
+#endif
+#endif
+
+//    glScalef(0.5F, 0.5F, 0.5F);
+
+    pushClipArea(Rect(0, 0, mRect.w, mRect.h));
+}
+
+void ModernOpenGLGraphics::endDraw()
+{
+    popClipArea();
+}
+
+void ModernOpenGLGraphics::prepareScreenshot()
+{
+    if (config.getBoolValue("usefbo"))
+        graphicsManager.createFBO(mRect.w, mRect.h, &mFbo);
+}
+
+SDL_Surface* ModernOpenGLGraphics::getScreenshot()
+{
+    const int h = mRect.h;
+    const int w = mRect.w - (mRect.w % 4);
+    GLint pack = 1;
+
+    SDL_Surface *const screenshot = MSDL_CreateRGBSurface(
+        SDL_SWSURFACE, w, h, 24,
+        0xff0000, 0x00ff00, 0x0000ff, 0x000000);
+
+    if (!screenshot)
+        return nullptr;
+
+    if (SDL_MUSTLOCK(screenshot))
+        SDL_LockSurface(screenshot);
+
+    const size_t lineSize = 3 * w;
+    GLubyte *const buf = new GLubyte[lineSize];
+
+    // Grap the pixel buffer and write it to the SDL surface
+    glGetIntegerv(GL_PACK_ALIGNMENT, &pack);
+    glPixelStorei(GL_PACK_ALIGNMENT, 1);
+    glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, screenshot->pixels);
+
+    // Flip the screenshot, as OpenGL has 0,0 in bottom left
+    const int h2 = h / 2;
+    for (int i = 0; i < h2; i++)
+    {
+        GLubyte *const top = static_cast<GLubyte*>(
+            screenshot->pixels) + lineSize * i;
+        GLubyte *const bot = static_cast<GLubyte*>(
+            screenshot->pixels) + lineSize * (h - 1 - i);
+
+        memcpy(buf, top, lineSize);
+        memcpy(top, bot, lineSize);
+        memcpy(bot, buf, lineSize);
+    }
+
+    delete [] buf;
+
+    if (config.getBoolValue("usefbo"))
+        graphicsManager.deleteFBO(&mFbo);
+
+    glPixelStorei(GL_PACK_ALIGNMENT, pack);
+
+    if (SDL_MUSTLOCK(screenshot))
+        SDL_UnlockSurface(screenshot);
+
+    return screenshot;
+}
+
+bool ModernOpenGLGraphics::pushClipArea(const Rect &area)
+{
+    int transX = 0;
+    int transY = 0;
+
+    if (!mClipStack.empty())
+    {
+        const ClipRect &clipArea = mClipStack.top();
+        transX = -clipArea.xOffset;
+        transY = -clipArea.yOffset;
+    }
+
+    const bool result = Graphics::pushClipArea(area);
+
+    const ClipRect &clipArea = mClipStack.top();
+    transX += clipArea.xOffset;
+    transY += clipArea.yOffset;
+
+    if (transX || transY)
+    {
+        glTranslatef(static_cast<GLfloat>(transX),
+                     static_cast<GLfloat>(transY), 0);
+    }
+    glScissor(clipArea.x * mScale,
+        (mRect.h - clipArea.y - clipArea.height) * mScale,
+        clipArea.width * mScale,
+        clipArea.height * mScale);
+    return result;
+}
+
+void ModernOpenGLGraphics::popClipArea()
+{
+    if (mClipStack.empty())
+        return;
+
+    const ClipRect &clipArea1 = mClipStack.top();
+    int transX = -clipArea1.xOffset;
+    int transY = -clipArea1.yOffset;
+
+    Graphics::popClipArea();
+
+    if (mClipStack.empty())
+        return;
+
+    const ClipRect &clipArea = mClipStack.top();
+    transX += clipArea.xOffset;
+    transY += clipArea.yOffset;
+    if (transX || transY)
+    {
+        glTranslatef(static_cast<GLfloat>(transX),
+                     static_cast<GLfloat>(transY), 0);
+    }
+    glScissor(clipArea.x * mScale,
+        (mRect.h - clipArea.y - clipArea.height) * mScale,
+        clipArea.width * mScale,
+        clipArea.height * mScale);
+}
+
+#ifdef ANDROID
+void ModernOpenGLGraphics::drawPoint(int x A_UNUSED, int y A_UNUSED)
+#else
+void ModernOpenGLGraphics::drawPoint(int x, int y)
+#endif
+{
+    setTexturingAndBlending(false);
+    restoreColor();
+
+#ifdef ANDROID
+    // TODO need fix
+#else
+    glBegin(GL_POINTS);
+    glVertex2i(x, y);
+    glEnd();
+#endif
+}
+
+void ModernOpenGLGraphics::drawLine(int x1, int y1, int x2, int y2)
+{
+    setTexturingAndBlending(false);
+    restoreColor();
+
+    mShortVertArray[0] = static_cast<GLshort>(x1);
+    mShortVertArray[1] = static_cast<GLshort>(y1);
+    mShortVertArray[2] = static_cast<GLshort>(x2);
+    mShortVertArray[3] = static_cast<GLshort>(y2);
+
+    drawLineArrays(4);
+}
+
+void ModernOpenGLGraphics::drawRectangle(const Rect& rect)
+{
+    drawRectangle(rect, false);
+}
+
+void ModernOpenGLGraphics::fillRectangle(const Rect& rect)
+{
+    drawRectangle(rect, true);
+}
+
+void ModernOpenGLGraphics::setTexturingAndBlending(const bool enable)
+{
+    if (enable)
+    {
+        if (!mTexture)
+        {
+            glEnable(OpenGLImageHelper::mTextureType);
+            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+            mTexture = true;
+        }
+
+        if (!mAlpha)
+        {
+            glEnable(GL_BLEND);
+            mAlpha = true;
+        }
+    }
+    else
+    {
+        mLastImage = 0;
+        if (mAlpha && !mColorAlpha)
+        {
+            glDisable(GL_BLEND);
+            mAlpha = false;
+        }
+        else if (!mAlpha && mColorAlpha)
+        {
+            glEnable(GL_BLEND);
+            mAlpha = true;
+        }
+
+        if (mTexture)
+        {
+            glDisable(OpenGLImageHelper::mTextureType);
+            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+            mTexture = false;
+        }
+    }
+}
+
+void ModernOpenGLGraphics::drawRectangle(const Rect& rect,
+                                         const bool filled)
+{
+    BLOCK_START("Graphics::drawRectangle")
+    setTexturingAndBlending(false);
+    restoreColor();
+
+    const GLshort x = static_cast<GLshort>(rect.x);
+    const GLshort y = static_cast<GLshort>(rect.y);
+    const GLshort width = static_cast<GLshort>(rect.width);
+    const GLshort height = static_cast<GLshort>(rect.height);
+    const GLshort xw = static_cast<GLshort>(rect.x + width);
+    const GLshort yh = static_cast<GLshort>(rect.y + height);
+
+    if (filled)
+    {
+        GLshort vert[] =
+        {
+            x, y,
+            xw, y,
+            x, yh,
+            xw, yh
+        };
+
+        glVertexPointer(2, GL_SHORT, 0, &vert);
+#ifdef DEBUG_DRAW_CALLS
+        mDrawCalls ++;
+#endif
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
+    else
+    {
+        GLshort vert[] =
+        {
+            x, y,
+            xw, y,
+            xw, yh,
+            x, yh
+        };
+
+        glVertexPointer(2, GL_SHORT, 0, &vert);
+#ifdef DEBUG_DRAW_CALLS
+        mDrawCalls ++;
+#endif
+        glDrawArrays(GL_LINE_LOOP, 0, 4);
+    }
+    BLOCK_END("Graphics::drawRectangle")
+}
+
+bool ModernOpenGLGraphics::drawNet(const int x1, const int y1,
+                                   const int x2, const int y2,
+                                   const int width, const int height)
+{
+    unsigned int vp = 0;
+    const unsigned int vLimit = mMaxVertices * 4;
+
+    setTexturingAndBlending(false);
+    restoreColor();
+
+    const GLshort xs1 = static_cast<GLshort>(x1);
+    const GLshort xs2 = static_cast<GLshort>(x2);
+    const GLshort ys1 = static_cast<GLshort>(y1);
+    const GLshort ys2 = static_cast<GLshort>(y2);
+    const int16_t width1 = static_cast<int16_t>(width);
+    const int16_t height1 = static_cast<int16_t>(height);
+
+    for (int16_t y = static_cast<int16_t>(y1); y < y2; y += height1)
+    {
+        mShortVertArray[vp + 0] = xs1;
+        mShortVertArray[vp + 1] = y;
+
+        mShortVertArray[vp + 2] = xs2;
+        mShortVertArray[vp + 3] = y;
+
+        vp += 4;
+        if (vp >= vLimit)
+        {
+            drawLineArrays(vp);
+            vp = 0;
+        }
+    }
+
+    for (int16_t x = static_cast<int16_t>(x1); x < x2; x += width1)
+    {
+        mShortVertArray[vp + 0] = x;
+        mShortVertArray[vp + 1] = ys1;
+
+        mShortVertArray[vp + 2] = x;
+        mShortVertArray[vp + 3] = ys2;
+
+        vp += 4;
+        if (vp >= vLimit)
+        {
+            drawLineArrays(vp);
+            vp = 0;
+        }
+    }
+
+    if (vp > 0)
+        drawLineArrays(vp);
+
+    return true;
+}
+
+void ModernOpenGLGraphics::bindTexture(const GLenum target,
+                                       const GLuint texture)
+{
+    if (mLastImage != texture)
+    {
+        mLastImage = texture;
+        glBindTexture(target, texture);
+    }
+}
+
+inline void ModernOpenGLGraphics::drawTriangleArrayfs(const int size)
+{
+    glVertexPointer(2, GL_SHORT, 0, mShortVertArray);
+    glTexCoordPointer(2, GL_FLOAT, 0, mFloatTexArray);
+
+#ifdef DEBUG_DRAW_CALLS
+    mDrawCalls ++;
+#endif
+    glDrawArrays(GL_TRIANGLES, 0, size / 2);
+}
+
+inline void ModernOpenGLGraphics::drawTriangleArrayfsCached(const int size)
+{
+    glVertexPointer(2, GL_SHORT, 0, mShortVertArrayCached);
+    glTexCoordPointer(2, GL_FLOAT, 0, mFloatTexArrayCached);
+
+#ifdef DEBUG_DRAW_CALLS
+    mDrawCalls ++;
+#endif
+    glDrawArrays(GL_TRIANGLES, 0, size / 2);
+}
+
+inline void ModernOpenGLGraphics::drawTriangleArrayfs(const GLshort *const
+                                                      shortVertArray,
+                                                      const GLfloat *const
+                                                      floatTexArray,
+                                                      const int size)
+{
+    glVertexPointer(2, GL_SHORT, 0, shortVertArray);
+    glTexCoordPointer(2, GL_FLOAT, 0, floatTexArray);
+
+#ifdef DEBUG_DRAW_CALLS
+    mDrawCalls ++;
+#endif
+    glDrawArrays(GL_TRIANGLES, 0, size / 2);
+}
+
+inline void ModernOpenGLGraphics::drawLineArrays(const int size)
+{
+    glVertexPointer(2, GL_SHORT, 0, mShortVertArray);
+
+#ifdef DEBUG_DRAW_CALLS
+    mDrawCalls ++;
+#endif
+    glDrawArrays(GL_LINES, 0, size / 2);
+}
+
+void ModernOpenGLGraphics::dumpSettings()
+{
+    GLint test[1000];
+    logger->log("\n\n");
+    logger->log("start opengl dump");
+    for (int f = 0; f < 65535; f ++)
+    {
+        test[0] = 0;
+        test[1] = 0;
+        test[2] = 0;
+        test[3] = 0;
+        glGetIntegerv(f, &test[0]);
+        if (test[0] || test[1] || test[2] || test[3])
+        {
+            logger->log("\n%d = %d, %d, %d, %d", f,
+                test[0], test[1], test[2], test[3]);
+        }
+    }
+}
+
+void ModernOpenGLGraphics::setColorAlpha(const float alpha)
+{
+    if (!mIsByteColor && mFloatColor == alpha)
+        return;
+
+    glColor4f(1.0F, 1.0F, 1.0F, alpha);
+    mIsByteColor = false;
+    mFloatColor = alpha;
+}
+
+void ModernOpenGLGraphics::restoreColor()
+{
+    if (mIsByteColor && mByteColor == mColor)
+        return;
+
+    glColor4ub(static_cast<GLubyte>(mColor.r),
+               static_cast<GLubyte>(mColor.g),
+               static_cast<GLubyte>(mColor.b),
+               static_cast<GLubyte>(mColor.a));
+    mIsByteColor = true;
+    mByteColor = mColor;
+}
+
+void ModernOpenGLGraphics::drawImageRect(const int x, const int y,
+                                         const int w, const int h,
+                                         const ImageRect &imgRect)
+{
+    #include "render/graphics_drawImageRect.hpp"
+}
+
+void ModernOpenGLGraphics::calcImageRect(ImageVertexes *const vert,
+                                         const int x, const int y,
+                                         const int w, const int h,
+                                         const ImageRect &imgRect)
+{
+    #include "render/graphics_calcImageRect.hpp"
+}
+
+void ModernOpenGLGraphics::clearScreen() const
+{
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+}
+
+#ifdef DEBUG_BIND_TEXTURE
+void ModernOpenGLGraphics::debugBindTexture(const Image *const image)
+{
+    const std::string texture = image->getIdPath();
+    if (mOldTexture != texture)
+    {
+        if ((!mOldTexture.empty() || !texture.empty())
+            && mOldTextureId != image->mGLImage)
+        {
+            logger->log("bind: %s (%d) to %s (%d)", mOldTexture.c_str(),
+                mOldTextureId, texture.c_str(), image->mGLImage);
+        }
+        mOldTextureId = image->mGLImage;
+        mOldTexture = texture;
+    }
+}
+#else
+void ModernOpenGLGraphics::debugBindTexture(const Image *const image A_UNUSED)
+{
+}
+#endif
+
+#endif  // USE_OPENGL
diff --git a/src/render/modernopenglgraphics.h b/src/render/modernopenglgraphics.h
new file mode 100644
index 000000000..b6a77640b
--- /dev/null
+++ b/src/render/modernopenglgraphics.h
@@ -0,0 +1,98 @@
+/*
+ *  The ManaPlus Client
+ *  Copyright (C) 2004-2009  The Mana World Development Team
+ *  Copyright (C) 2009-2010  The Mana Developers
+ *  Copyright (C) 2011-2014  The ManaPlus Developers
+ *
+ *  This file is part of The ManaPlus Client.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RENDER_MODERNOPENGLGRAPHICS_H
+#define RENDER_MODERNOPENGLGRAPHICS_H
+
+#include "main.h"
+#ifdef USE_OPENGL
+
+#include "localconsts.h"
+#include "render/graphics.h"
+
+#include "resources/fboinfo.h"
+
+#ifdef ANDROID
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#else
+#ifndef USE_SDL2
+#define GL_GLEXT_PROTOTYPES 1
+#endif
+#include <SDL_opengl.h>
+#include <GL/glext.h>
+#endif
+
+#include <set>
+
+class NormalOpenGLGraphicsVertexes;
+
+class ModernOpenGLGraphics final : public Graphics
+{
+    public:
+        ModernOpenGLGraphics();
+
+        A_DELETE_COPY(ModernOpenGLGraphics)
+
+        ~ModernOpenGLGraphics();
+
+        inline void drawTriangleArrayfs(const GLshort *const shortVertArray,
+                                        const GLfloat *const floatTexArray,
+                                        const int size);
+
+        inline void drawTriangleArrayfs(const int size);
+
+        inline void drawTriangleArrayfsCached(const int size);
+
+        inline void drawLineArrays(const int size);
+
+        #include "render/graphicsdef.hpp"
+
+        #include "render/openglgraphicsdef.hpp"
+
+        #include "render/openglgraphicsdefadvanced.hpp"
+
+    private:
+        GLfloat *mFloatTexArray;
+        GLshort *mShortVertArray;
+        GLfloat *mFloatTexArrayCached;
+        GLshort *mShortVertArrayCached;
+        float mAlphaCached;
+        int mVpCached;
+        bool mTexture;
+
+        bool mIsByteColor;
+        Color mByteColor;
+        GLuint mImageCached;
+        float mFloatColor;
+        int mMaxVertices;
+        bool mColorAlpha;
+#ifdef DEBUG_BIND_TEXTURE
+        std::string mOldTexture;
+        unsigned mOldTextureId;
+#endif
+        FBOInfo mFbo;
+};
+#endif
+
+#endif  // RENDER_MODERNOPENGLGRAPHICS_H
diff --git a/src/resources/image.h b/src/resources/image.h
index cd664cebb..2c67281b7 100644
--- a/src/resources/image.h
+++ b/src/resources/image.h
@@ -69,6 +69,7 @@ class Image notfinal : public Resource
 #ifdef USE_OPENGL
     friend class AtlasManager;
     friend class MobileOpenGLGraphics;
+    friend class ModernOpenGLGraphics;
     friend class NormalOpenGLGraphics;
     friend class NullOpenGLGraphics;
     friend class SafeOpenGLGraphics;
diff --git a/src/resources/openglimagehelper.cpp b/src/resources/openglimagehelper.cpp
index ca5f6410a..2ce4bd405 100644
--- a/src/resources/openglimagehelper.cpp
+++ b/src/resources/openglimagehelper.cpp
@@ -29,6 +29,7 @@
 
 #include "render/mgl.h"
 #include "render/mobileopenglgraphics.h"
+#include "render/modernopenglgraphics.h"
 #include "render/normalopenglgraphics.h"
 #include "render/safeopenglgraphics.h"
 
@@ -202,9 +203,11 @@ Image *OpenGLImageHelper::glLoad(SDL_Surface *tmpImage,
     {
 #ifndef ANDROID
         case RENDER_NORMAL_OPENGL:
-        case RENDER_MODERN_OPENGL:
             NormalOpenGLGraphics::bindTexture(mTextureType, texture);
             break;
+        case RENDER_MODERN_OPENGL:
+            ModernOpenGLGraphics::bindTexture(mTextureType, texture);
+            break;
         case RENDER_SAFE_OPENGL:
             SafeOpenGLGraphics::bindTexture(mTextureType, texture);
             break;
-- 
cgit v1.2.3-70-g09d2