Rice Video Plugin for GLES1.1
[mupen64plus-pandora.git] / source / rice_gles / src / OGLExtCombiner.cpp
diff --git a/source/rice_gles/src/OGLExtCombiner.cpp b/source/rice_gles/src/OGLExtCombiner.cpp
new file mode 100644 (file)
index 0000000..b7702e9
--- /dev/null
@@ -0,0 +1,1263 @@
+/*
+Copyright (C) 2003 Rice1964
+
+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 (at your option) 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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*/
+
+#include <algorithm>
+#include "osal_opengl.h"
+
+#if SDL_VIDEO_OPENGL
+#include "OGLExtensions.h"
+#endif
+#include "OGLDebug.h"
+#include "OGLExtCombiner.h"
+#include "OGLExtRender.h"
+#include "OGLDecodedMux.h"
+#include "OGLGraphicsContext.h"
+#include "OGLTexture.h"
+#include "DirectXDecodedMux.h"
+
+#define GL_MODULATE_ADD_ATI        0x8744
+#define GL_MODULATE_SUBTRACT_ATI   0x8746
+
+//========================================================================
+COGLColorCombiner4::COGLColorCombiner4(CRender *pRender)
+        :COGLColorCombiner(pRender), m_maxTexUnits(0), m_lastIndex(-1),
+        m_dwLastMux0(0), m_dwLastMux1(0)
+{
+    m_bOGLExtCombinerSupported=false;
+    m_bSupportModAdd_ATI = false;
+    m_bSupportModSub_ATI = false;
+    delete m_pDecodedMux;
+    m_pDecodedMux = new COGLExtDecodedMux;
+}
+
+COGLColorCombiner4v2::COGLColorCombiner4v2(CRender *pRender)
+    :COGLColorCombiner4(pRender)
+{
+    delete m_pDecodedMux;
+    m_pDecodedMux = new DecodedMuxForOGL14V2;
+}
+
+COGLColorCombiner2::COGLColorCombiner2(CRender *pRender)
+        :COGLColorCombiner4(pRender)
+{
+    delete m_pDecodedMux;
+    m_pDecodedMux = new CDirectXDecodedMux;     // Use Mux for DirectX because we support only 1 texture for each stage
+    m_ppGeneralDecodedMux = &m_pDecodedMux;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool COGLColorCombiner4::Initialize(void)
+{
+    m_bOGLExtCombinerSupported = false;
+    m_bSupportModAdd_ATI = false;
+    m_bSupportModSub_ATI = false;
+    m_maxTexUnits = 1;
+
+#if SDL_VIDEO_OPENGL
+    if( COGLColorCombiner::Initialize() )
+    {
+        m_bSupportMultiTexture = true;
+        COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext);
+#ifndef HAVE_GLES
+        if( pcontext->IsExtensionSupported("GL_EXT_texture_env_combine") || pcontext->IsExtensionSupported("GL_ARB_texture_env_combine") )
+#endif
+        {
+            m_bOGLExtCombinerSupported = true;
+            glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&m_maxTexUnits);
+            OPENGL_CHECK_ERRORS;
+            if( m_maxTexUnits > 8 ) m_maxTexUnits = 8;
+
+            TRACE0("Starting Ogl 1.4 multitexture combiner" );
+            TRACE1("m_maxTexUnits = %d", m_maxTexUnits);
+#ifndef HAVE_GLES
+            if( pcontext->IsExtensionSupported("ATI_texture_env_combine3") )
+            {
+                m_bSupportModAdd_ATI = true;
+                m_bSupportModSub_ATI = true;
+            }
+#endif
+        }
+#ifndef HAVE_GLES
+        else
+        {
+            DebugMessage(M64MSG_ERROR, "Your video card does not support OpenGL extension combiner, you can only use the basic OpenGL combiner functions");
+        }
+#endif
+        m_supportedStages = m_maxTexUnits;
+        return true;
+    }
+    return false;
+
+#elif SDL_VIDEO_OPENGL_ES2
+    return true;
+#endif
+}
+
+bool COGLColorCombiner2::Initialize(void)
+{
+    TRACE0("Starting Ogl 1.2/1.3 multitexture combiner" );
+    if( COGLColorCombiner4::Initialize() )
+    {
+        // For general combiner flags
+        m_dwGeneralMaxStages = m_supportedStages;
+
+        m_bTxtOpAdd = m_bSupportAdd;
+        m_bTxtOpSub = m_bSupportSubtract;
+        m_bTxtOpLerp = true;
+
+        m_bTxtOpAddSmooth = true;
+        m_bTxtOpBlendCurAlpha = true;
+        m_bTxtOpBlendDifAlpha = true;
+        m_bTxtOpBlendFacAlpha = true;
+        m_bTxtOpBlendTxtAlpha = true;
+        m_bTxtOpMulAdd = m_bSupportModAdd_ATI;
+
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+//========================================================================
+void COGLColorCombiner4::InitCombinerCycleFill(void)
+{
+    for( int i=0; i<m_supportedStages; i++ )
+    {
+        pglActiveTexture(GL_TEXTURE0_ARB+i);
+        OPENGL_CHECK_ERRORS;
+        m_pOGLRender->EnableTexUnit(i,FALSE);
+    }
+
+    //pglActiveTexture(GL_TEXTURE0_ARB);
+    //m_pOGLRender->EnableTexUnit(0,FALSE);
+    //pglActiveTexture(GL_TEXTURE1_ARB);
+    //m_pOGLRender->EnableTexUnit(1,FALSE);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void COGLColorCombiner4::InitCombinerCycle12(void)
+{
+    if( !m_bOGLExtCombinerSupported )   
+    {
+        COGLColorCombiner::InitCombinerCycle12();
+        return;
+    }
+
+#ifdef DEBUGGER
+    if( debuggerDropCombiners )
+    {
+        UpdateCombiner(m_pDecodedMux->m_dwMux0,m_pDecodedMux->m_dwMux1);
+        m_vCompiledSettings.clear();
+        m_dwLastMux0 = m_dwLastMux1 = 0;
+        debuggerDropCombiners = false;
+    }
+#endif
+
+    m_pOGLRender->EnableMultiTexture();
+
+    bool combinerIsChanged = false;
+
+    if( m_pDecodedMux->m_dwMux0 != m_dwLastMux0 || m_pDecodedMux->m_dwMux1 != m_dwLastMux1 || m_lastIndex < 0 )
+    {
+        combinerIsChanged = true;
+        m_lastIndex = FindCompiledMux();
+        if( m_lastIndex < 0 )       // Can not found
+        {
+            m_lastIndex = ParseDecodedMux();
+#ifdef DEBUGGER
+            DisplaySimpleMuxString();
+#endif
+        }
+
+        m_dwLastMux0 = m_pDecodedMux->m_dwMux0;
+        m_dwLastMux1 = m_pDecodedMux->m_dwMux1;
+    }
+    
+
+    if( m_bCycleChanged || combinerIsChanged || gRDP.texturesAreReloaded || gRDP.colorsAreReloaded )
+    {
+        if( m_bCycleChanged || combinerIsChanged )
+        {
+            GenerateCombinerSettingConstants(m_lastIndex);
+            GenerateCombinerSetting(m_lastIndex);
+        }
+        else if( gRDP.colorsAreReloaded )
+        {
+            GenerateCombinerSettingConstants(m_lastIndex);
+        }
+
+        m_pOGLRender->SetAllTexelRepeatFlag();
+
+        gRDP.colorsAreReloaded = false;
+        gRDP.texturesAreReloaded = false;
+    }
+    else
+    {
+        m_pOGLRender->SetAllTexelRepeatFlag();
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////
+int COGLColorCombiner4::ParseDecodedMux()
+{
+#define nextUnit()  {unitNo++;}
+#if SDL_VIDEO_OPENGL
+    if( m_maxTexUnits<3) 
+        return  ParseDecodedMux2Units();
+
+    OGLExtCombinerSaveType res;
+    for( int k=0; k<8; k++ )
+        res.units[k].tex = -1;
+    
+    COGLDecodedMux &mux = *(COGLDecodedMux*)m_pDecodedMux;
+
+    int unitNos[2];
+    for( int rgbalpha = 0; rgbalpha<2; rgbalpha++ )
+    {
+        unitNos[rgbalpha] = 0;
+        for( int cycle = 0; cycle<2; cycle++ )
+        {
+            int &unitNo = unitNos[rgbalpha];
+            OGLExtCombinerType &unit = res.units[unitNo];
+            OGLExt1CombType &comb = unit.Combs[rgbalpha];
+            CombinerFormatType type = m_pDecodedMux->splitType[cycle*2+rgbalpha];
+            N64CombinerType &m = m_pDecodedMux->m_n64Combiners[cycle*2+rgbalpha];
+            comb.arg0 = comb.arg1 = comb.arg2 = CM_IGNORE_BYTE;
+
+            switch( type )
+            {
+            case CM_FMT_TYPE_NOT_USED:
+                comb.arg0 = MUX_COMBINED;
+                unit.ops[rgbalpha] = GL_REPLACE;
+                nextUnit();
+                break;
+            case CM_FMT_TYPE_D:                 // = A
+                comb.arg0 = m.d;
+                unit.ops[rgbalpha] = GL_REPLACE;
+                nextUnit();
+                break;
+            case CM_FMT_TYPE_A_ADD_D:           // = A+D
+                comb.arg0 = m.a;
+                comb.arg1 = m.d;
+                unit.ops[rgbalpha] = GL_ADD;
+                nextUnit();
+                break;
+            case CM_FMT_TYPE_A_SUB_B:           // = A-B
+                comb.arg0 = m.a;
+                comb.arg1 = m.b;
+                unit.ops[rgbalpha] = GL_SUBTRACT_ARB;
+                nextUnit();
+                break;
+            case CM_FMT_TYPE_A_MOD_C:           // = A*C
+                comb.arg0 = m.a;
+                comb.arg1 = m.c;
+                unit.ops[rgbalpha] = GL_MODULATE;
+                nextUnit();
+                break;
+            case CM_FMT_TYPE_A_MOD_C_ADD_D:     // = A*C+D
+                if( m_bSupportModAdd_ATI )
+                {
+                    comb.arg0 = m.a;
+                    comb.arg2 = m.c;
+                    comb.arg1 = m.d;
+                    unit.ops[rgbalpha] = GL_MODULATE_ADD_ATI;
+                    nextUnit();
+                }
+                else
+                {
+                    if( unitNo < m_maxTexUnits-1 )
+                    {
+                        comb.arg0 = m.a;
+                        comb.arg1 = m.c;
+                        unit.ops[rgbalpha] = GL_MODULATE;
+                        nextUnit();
+                        res.units[unitNo].Combs[rgbalpha].arg0 = MUX_COMBINED;
+                        res.units[unitNo].Combs[rgbalpha].arg1 = m.d;
+                        res.units[unitNo].ops[rgbalpha] = GL_ADD;
+                        nextUnit();
+                    }
+                    else
+                    {
+                        comb.arg0 = m.a;
+                        comb.arg1 = m.c;
+                        comb.arg2 = m.d;
+                        unit.ops[rgbalpha] = GL_INTERPOLATE_ARB;
+                        nextUnit();
+                    }
+                }
+                break;
+            case CM_FMT_TYPE_A_LERP_B_C:        // = (A-B)*C+B
+                comb.arg0 = m.a;
+                comb.arg1 = m.b;
+                comb.arg2 = m.c;
+                unit.ops[rgbalpha] = GL_INTERPOLATE_ARB;
+                nextUnit();
+                break;
+            case CM_FMT_TYPE_A_SUB_B_ADD_D:     // = A-B+D
+                if( unitNo < m_maxTexUnits-1 )
+                {
+                    comb.arg0 = m.a;
+                    comb.arg1 = m.b;
+                    unit.ops[rgbalpha] = GL_SUBTRACT_ARB;
+                    nextUnit();
+                    res.units[unitNo].Combs[rgbalpha].arg0 = MUX_COMBINED;
+                    res.units[unitNo].Combs[rgbalpha].arg1 = m.d;
+                    res.units[unitNo].ops[rgbalpha] = GL_ADD;
+                    nextUnit();
+                }
+                else
+                {
+                    comb.arg0 = m.a;
+                    comb.arg1 = m.c;
+                    comb.arg2 = m.d;
+                    unit.ops[rgbalpha] = GL_INTERPOLATE_ARB;
+                    nextUnit();
+                }
+                break;
+            case CM_FMT_TYPE_A_SUB_B_MOD_C:     // = (A-B)*C
+                if( unitNo < m_maxTexUnits-1 )
+                {
+                    comb.arg0 = m.a;
+                    comb.arg1 = m.b;
+                    unit.ops[rgbalpha] = GL_SUBTRACT_ARB;
+                    nextUnit();
+                    res.units[unitNo].Combs[rgbalpha].arg0 = MUX_COMBINED;
+                    res.units[unitNo].Combs[rgbalpha].arg1 = m.c;
+                    res.units[unitNo].ops[rgbalpha] = GL_MODULATE;
+                    nextUnit();
+                }
+                else
+                {
+                    comb.arg0 = m.a;
+                    comb.arg1 = m.c;
+                    comb.arg2 = m.d;
+                    unit.ops[rgbalpha] = GL_INTERPOLATE_ARB;
+                    nextUnit();
+                }
+                break;
+            case CM_FMT_TYPE_A_B_C_D:           // = (A-B)*C+D
+            default:
+                if( unitNo < m_maxTexUnits-1 )
+                {
+                    comb.arg0 = m.a;
+                    comb.arg1 = m.b;
+                    unit.ops[rgbalpha] = GL_SUBTRACT_ARB;
+                    nextUnit();
+                    if( m_bSupportModAdd_ATI )
+                    {
+                        res.units[unitNo].Combs[rgbalpha].arg0 = MUX_COMBINED;
+                        res.units[unitNo].Combs[rgbalpha].arg2 = m.c;
+                        res.units[unitNo].Combs[rgbalpha].arg1 = m.d;
+                        res.units[unitNo].ops[rgbalpha] = GL_MODULATE_ADD_ATI;
+                        nextUnit();
+                    }
+                    else
+                    {
+                        res.units[unitNo].Combs[rgbalpha].arg0 = m.a;
+                        res.units[unitNo].Combs[rgbalpha].arg1 = m.b;
+                        res.units[unitNo].Combs[rgbalpha].arg2 = m.c;
+                        res.units[unitNo].ops[rgbalpha] = GL_INTERPOLATE_ARB;
+                        nextUnit();
+                    }
+                }
+                else
+                {
+                    comb.arg0 = m.a;
+                    comb.arg1 = m.c;
+                    comb.arg2 = m.d;
+                    unit.ops[rgbalpha] = GL_INTERPOLATE_ARB;
+                    nextUnit();
+                }
+                break;
+            }
+        }
+    }
+        
+    res.numOfUnits = min(m_maxTexUnits, max(unitNos[0],unitNos[1]));
+
+    if( unitNos[0]>m_maxTexUnits || unitNos[1]>m_maxTexUnits ) 
+    {
+        TRACE0("Unit overflows");
+    }
+
+    for( int j=0; j<2; j++ )
+    {
+        if( unitNos[j]<res.numOfUnits )
+        {
+            for( int i=unitNos[j]; i<res.numOfUnits; i++ )
+            {
+                res.units[i].Combs[j].arg0 = MUX_COMBINED;
+                res.units[i].ops[j] = GL_REPLACE;
+            }
+        }
+    }
+
+    res.units[0].tex = 0;
+    res.units[1].tex = 1;
+
+    res.primIsUsed = mux.isUsed(MUX_PRIM);
+    res.envIsUsed = mux.isUsed(MUX_ENV);
+    res.lodFracIsUsed = mux.isUsed(MUX_LODFRAC) || mux.isUsed(MUX_PRIMLODFRAC);
+
+    return SaveParsedResult(res);
+
+#elif SDL_VIDEO_OPENGL_ES2
+    return 0;
+#endif
+}
+
+int COGLColorCombiner4::ParseDecodedMux2Units()
+{
+    OGLExtCombinerSaveType res;
+    for( int k=0; k<8; k++ )
+        res.units[k].tex = -1;
+
+    res.numOfUnits = 2;
+
+    for( int i=0; i<res.numOfUnits*2; i++ ) // Set combiner for each texture unit
+    {
+        // For each texture unit, set both RGB and Alpha channel
+        // Keep in mind that the m_pDecodeMux has been reformated and simplified very well
+
+        OGLExtCombinerType &unit = res.units[i/2];
+        OGLExt1CombType &comb = unit.Combs[i%2];
+
+        CombinerFormatType type = m_pDecodedMux->splitType[i];
+        N64CombinerType &m = m_pDecodedMux->m_n64Combiners[i];
+
+        comb.arg0 = comb.arg1 = comb.arg2 = MUX_0;
+
+        switch( type )
+        {
+        case CM_FMT_TYPE_NOT_USED:
+            comb.arg0 = MUX_COMBINED;
+            unit.ops[i%2] = GL_REPLACE;
+            break;
+        case CM_FMT_TYPE_D:                 // = A
+            comb.arg0 = m.d;
+            unit.ops[i%2] = GL_REPLACE;
+            break;
+#if SDL_VIDEO_OPENGL
+        case CM_FMT_TYPE_A_ADD_D:           // = A+D
+            comb.arg0 = m.a;
+            comb.arg1 = m.d;
+            unit.ops[i%2] = GL_ADD;
+            break;
+        case CM_FMT_TYPE_A_SUB_B:           // = A-B
+            comb.arg0 = m.a;
+            comb.arg1 = m.b;
+            unit.ops[i%2] = GL_SUBTRACT_ARB;
+            break;
+        case CM_FMT_TYPE_A_MOD_C:           // = A*C
+            comb.arg0 = m.a;
+            comb.arg1 = m.c;
+            unit.ops[i%2] = GL_MODULATE;
+            break;
+        case CM_FMT_TYPE_A_MOD_C_ADD_D:     // = A*C+D
+            comb.arg0 = m.a;
+            comb.arg1 = m.c;
+            comb.arg2 = m.d;
+            unit.ops[i%2] = GL_INTERPOLATE_ARB;
+            break;
+        case CM_FMT_TYPE_A_LERP_B_C:        // = (A-B)*C+B
+            comb.arg0 = m.a;
+            comb.arg1 = m.b;
+            comb.arg2 = m.c;
+            unit.ops[i%2] = GL_INTERPOLATE_ARB;
+            break;
+        case CM_FMT_TYPE_A_SUB_B_ADD_D:     // = A-B+D
+            // fix me, to use 2 texture units
+            comb.arg0 = m.a;
+            comb.arg1 = m.b;
+            unit.ops[i%2] = GL_SUBTRACT_ARB;
+            break;
+        case CM_FMT_TYPE_A_SUB_B_MOD_C:     // = (A-B)*C
+            // fix me, to use 2 texture units
+            comb.arg0 = m.a;
+            comb.arg1 = m.c;
+            unit.ops[i%2] = GL_MODULATE;
+            break;
+            break;
+        case CM_FMT_TYPE_A_B_C_D:           // = (A-B)*C+D
+#endif
+        default:
+            comb.arg0 = m.a;
+            comb.arg1 = m.b;
+            comb.arg2 = m.c;
+            unit.ops[i%2] = GL_INTERPOLATE_ARB;
+            break;
+        }
+    }
+
+    if( m_pDecodedMux->splitType[2] == CM_FMT_TYPE_NOT_USED && m_pDecodedMux->splitType[3] == CM_FMT_TYPE_NOT_USED && !m_bTex1Enabled )
+    {
+        res.numOfUnits = 1;
+    }
+
+    res.units[0].tex = 0;
+    res.units[1].tex = 1;
+
+    return SaveParsedResult(res);
+}
+
+const char* COGLColorCombiner4::GetOpStr(GLenum op)
+{
+    switch( op )
+    {
+    case GL_REPLACE:
+        return "REPLACE";
+#if SDL_VIDEO_OPENGL
+    case GL_MODULATE:
+        return "MOD";
+    case GL_ADD:
+        return "ADD";
+    case GL_ADD_SIGNED_ARB:
+        return "ADD_SIGNED";
+    case GL_INTERPOLATE_ARB:
+        return "INTERPOLATE";
+    case GL_SUBTRACT_ARB:
+        return "SUB";
+#endif
+    case GL_MODULATE_ADD_ATI:
+        return "MULADD";
+    default:
+        return "SUB";
+    }
+}
+
+int COGLColorCombiner4::SaveParsedResult(OGLExtCombinerSaveType &result)
+{
+    result.dwMux0 = m_pDecodedMux->m_dwMux0;
+    result.dwMux1 = m_pDecodedMux->m_dwMux1;
+
+    for( int n=0; n<result.numOfUnits; n++ )
+    {
+        for( int i=0; i<3; i++ )
+        {
+            result.units[n].glRGBArgs[i] = 0;
+            result.units[n].glRGBFlags[i] = 0;
+            result.units[n].glAlphaArgs[i] = 0;
+            result.units[n].glAlphaFlags[i] = 0;
+            if( result.units[n].rgbComb.args[i] != CM_IGNORE_BYTE )
+            {
+                result.units[n].glRGBArgs[i] = MapRGBArgs(result.units[n].rgbComb.args[i]);
+                result.units[n].glRGBFlags[i] = MapRGBArgFlags(result.units[n].rgbComb.args[i]);
+            }
+            if( result.units[n].alphaComb.args[i] != CM_IGNORE_BYTE )
+            {
+                result.units[n].glAlphaArgs[i] = MapAlphaArgs(result.units[n].alphaComb.args[i]);
+                result.units[n].glAlphaFlags[i] = MapAlphaArgFlags(result.units[n].alphaComb.args[i]);
+            }
+        }
+    }
+
+    m_vCompiledSettings.push_back(result);
+    m_lastIndex = m_vCompiledSettings.size()-1;
+
+#ifdef DEBUGGER
+    if( logCombiners )
+    {
+        DisplaySimpleMuxString();
+    }
+#endif
+
+    return m_lastIndex;
+}
+
+bool isGLtex(GLint val)
+{
+    if( val >= GL_TEXTURE0_ARB && val <= GL_TEXTURE7_ARB )
+        return true;
+    else
+        return false;
+}
+
+int COGLColorCombiner4v2::SaveParsedResult(OGLExtCombinerSaveType &result)
+{
+    result.dwMux0 = m_pDecodedMux->m_dwMux0;
+    result.dwMux1 = m_pDecodedMux->m_dwMux1;
+
+    int n;
+
+    for( n=0; n<result.numOfUnits; n++ )
+    {
+        for( int i=0; i<3; i++ )
+        {
+            result.units[n].glRGBArgs[i] = 0;
+            result.units[n].glRGBFlags[i] = 0;
+            result.units[n].glAlphaArgs[i] = 0;
+            result.units[n].glAlphaFlags[i] = 0;
+            if( result.units[n].rgbComb.args[i] != CM_IGNORE_BYTE )
+            {
+                result.units[n].glRGBArgs[i] = MapRGBArgs(result.units[n].rgbComb.args[i]);
+                if( result.units[n].glRGBArgs[i] == GL_TEXTURE3_ARB && !result.envIsUsed )
+                    result.units[n].glRGBArgs[i] = GL_TEXTURE2_ARB;
+
+                result.units[n].glRGBFlags[i] = MapRGBArgFlags(result.units[n].rgbComb.args[i]);
+            }
+            if( result.units[n].alphaComb.args[i] != CM_IGNORE_BYTE )
+            {
+                result.units[n].glAlphaArgs[i] = MapAlphaArgs(result.units[n].alphaComb.args[i]);
+                if( result.units[n].glAlphaArgs[i] == GL_TEXTURE3_ARB && !result.envIsUsed )
+                    result.units[n].glAlphaArgs[i] = GL_TEXTURE2_ARB;
+
+                result.units[n].glAlphaFlags[i] = MapAlphaArgFlags(result.units[n].alphaComb.args[i]);
+            }
+        }
+
+        if( isGLtex(result.units[n].glRGBArgs[0]) && isGLtex(result.units[n].glRGBArgs[1]) && isGLtex(result.units[n].glRGBArgs[2]) )
+        {
+            result.units[n].glRGBArgs[2] = GL_CONSTANT_ARB;
+        }
+        if( isGLtex(result.units[n].glAlphaArgs[0]) && isGLtex(result.units[n].glAlphaArgs[1]) && isGLtex(result.units[n].glAlphaArgs[2]) )
+        {
+            result.units[n].glRGBArgs[2] = GL_CONSTANT_ARB;
+        }
+    }
+
+    int extraUnit = 0;
+    if( result.envIsUsed )  extraUnit++;
+    if( result.lodFracIsUsed ) extraUnit++;
+    for( n=result.numOfUnits; n<result.numOfUnits+extraUnit; n++ )
+    {
+        for( int i=0; i<3; i++ )
+        {
+            result.units[n].rgbComb.args[i]=CM_IGNORE_BYTE;
+            result.units[n].alphaComb.args[i]=CM_IGNORE_BYTE;
+            result.units[n].glRGBArgs[i] = 0;
+            result.units[n].glRGBFlags[i] = 0;
+            result.units[n].glAlphaArgs[i] = 0;
+            result.units[n].glAlphaFlags[i] = 0;
+        }
+        
+        result.units[n].rgbComb.args[0]=MUX_COMBINED;
+        result.units[n].alphaComb.args[0]=MUX_COMBINED;
+        result.units[n].rgbOp = GL_REPLACE;
+        result.units[n].alphaOp = GL_REPLACE;
+        result.units[n].glRGBArgs[0] = GL_PREVIOUS_ARB;
+        result.units[n].glRGBArgs[1] = GL_PREVIOUS_ARB;
+        result.units[n].rgbFlag0gl = GL_SRC_COLOR;
+        result.units[n].rgbFlag1gl = GL_SRC_COLOR;
+        result.units[n].glAlphaArgs[0] = GL_PREVIOUS_ARB;
+        result.units[n].glAlphaArgs[1] = GL_PREVIOUS_ARB;
+        result.units[n].alphaFlag0gl = GL_SRC_ALPHA;
+        result.units[n].alphaFlag1gl = GL_SRC_ALPHA;
+    }
+
+    result.numOfUnits += extraUnit;
+
+    m_vCompiledSettings.push_back(result);
+    m_lastIndex = m_vCompiledSettings.size()-1;
+
+#ifdef DEBUGGER
+    if( logCombiners )
+    {
+        DisplaySimpleMuxString();
+    }
+#endif
+
+    return m_lastIndex;
+}
+
+
+#ifdef DEBUGGER
+extern const char *translatedCombTypes[];
+void COGLColorCombiner4::DisplaySimpleMuxString(void)
+{
+    char buf0[30], buf1[30], buf2[30];
+    OGLExtCombinerSaveType &result = m_vCompiledSettings[m_lastIndex];
+
+    COGLColorCombiner::DisplaySimpleMuxString();
+    DebuggerAppendMsg("OpenGL 1.2: %d Stages", result.numOfUnits);      
+    for( int i=0; i<result.numOfUnits; i++ )
+    {
+        DebuggerAppendMsg("//aRGB%d:\t%s: %s, %s, %s\n", i,GetOpStr(result.units[i].rgbOp), DecodedMux::FormatStr(result.units[i].rgbArg0,buf0), DecodedMux::FormatStr(result.units[i].rgbArg1,buf1), DecodedMux::FormatStr(result.units[i].rgbArg2,buf2));     
+        DebuggerAppendMsg("//aAlpha%d:\t%s: %s, %s, %s\n", i,GetOpStr(result.units[i].alphaOp), DecodedMux::FormatStr(result.units[i].alphaArg0,buf0), DecodedMux::FormatStr(result.units[i].alphaArg1,buf1), DecodedMux::FormatStr(result.units[i].alphaArg2,buf2));       
+    }
+    TRACE0("\n\n");
+}
+void COGLColorCombiner2::DisplaySimpleMuxString(void)
+{
+    char buf0[30], buf1[30], buf2[30];
+    OGLExtCombinerSaveType &result = m_vCompiledSettings[m_lastIndex];
+
+    COGLColorCombiner::DisplaySimpleMuxString();
+    int generalCombinerIndex = CGeneralCombiner::FindCompiledMux();
+    if( generalCombinerIndex < 0 )      // Can not found
+    {
+        generalCombinerIndex = CGeneralCombiner::ParseDecodedMux();
+    }
+    DebuggerAppendMsg("Generated general combiners:");
+    GeneralCombinerInfo &generalRes = m_vCompiledCombinerStages[generalCombinerIndex];
+    General_DisplayBlendingStageInfo(generalRes);
+
+    DebuggerAppendMsg("OpenGL 1.2: %d Stages", result.numOfUnits);      
+    for( int i=0; i<result.numOfUnits; i++ )
+    {
+        DebuggerAppendMsg("//aRGB%d:\t%s: %s, %s, %s\n", i,GetOpStr(result.units[i].rgbOp), DecodedMux::FormatStr(result.units[i].rgbArg0,buf0), DecodedMux::FormatStr(result.units[i].rgbArg1,buf1), DecodedMux::FormatStr(result.units[i].rgbArg2,buf2));     
+        DebuggerAppendMsg("//aAlpha%d:\t%s: %s, %s, %s\n", i,GetOpStr(result.units[i].alphaOp), DecodedMux::FormatStr(result.units[i].alphaArg0,buf0), DecodedMux::FormatStr(result.units[i].alphaArg1,buf1), DecodedMux::FormatStr(result.units[i].alphaArg2,buf2));       
+    }
+    TRACE0("\n\n");
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////
+int COGLColorCombiner4::FindCompiledMux()
+{
+#ifdef DEBUGGER
+    if( debuggerDropCombiners )
+    {
+        m_vCompiledSettings.clear();
+        //m_dwLastMux0 = m_dwLastMux1 = 0;
+        debuggerDropCombiners = false;
+    }
+#endif
+    for( uint32 i=0; i<m_vCompiledSettings.size(); i++ )
+    {
+        if( m_vCompiledSettings[i].dwMux0 == m_pDecodedMux->m_dwMux0 && m_vCompiledSettings[i].dwMux1 == m_pDecodedMux->m_dwMux1 )
+            return (int)i;
+    }
+
+    return -1;
+}
+
+//========================================================================
+
+GLint COGLColorCombiner4::RGBArgsMap4[] =
+{
+#if SDL_VIDEO_OPENGL
+    GL_PRIMARY_COLOR_ARB,           //MUX_0
+    GL_PRIMARY_COLOR_ARB,           //MUX_1
+    GL_PREVIOUS_ARB,                //MUX_COMBINED,
+#endif
+    GL_TEXTURE0_ARB,                //MUX_TEXEL0,
+#if SDL_VIDEO_OPENGL
+    GL_TEXTURE1_ARB,                //MUX_TEXEL1,
+    GL_CONSTANT_ARB,                //MUX_PRIM,
+    GL_PRIMARY_COLOR_ARB,           //MUX_SHADE,
+    GL_CONSTANT_ARB,                //MUX_ENV,
+    GL_PREVIOUS_ARB,                //MUX_COMBALPHA,
+#endif
+    GL_TEXTURE0_ARB,                //MUX_T0_ALPHA,
+#if SDL_VIDEO_OPENGL
+    GL_TEXTURE1_ARB,                //MUX_T1_ALPHA,
+    GL_CONSTANT_ARB,                //MUX_PRIM_ALPHA,
+    GL_PRIMARY_COLOR_ARB,           //MUX_SHADE_ALPHA,
+    GL_CONSTANT_ARB,                //MUX_ENV_ALPHA,
+    GL_CONSTANT_ARB,                //MUX_LODFRAC,
+    GL_CONSTANT_ARB,                //MUX_PRIMLODFRAC,
+    GL_PRIMARY_COLOR_ARB,           //MUX_K5
+    GL_PRIMARY_COLOR_ARB            //MUX_UNK
+#endif
+};
+
+GLint COGLColorCombiner4v2::RGBArgsMap4v2[] =
+{
+#if SDL_VIDEO_OPENGL
+    GL_PRIMARY_COLOR_ARB,           //MUX_0
+    GL_PRIMARY_COLOR_ARB,           //MUX_1
+    GL_PREVIOUS_ARB,                //MUX_COMBINED,
+#endif
+    GL_TEXTURE0_ARB,                //MUX_TEXEL0,
+#if SDL_VIDEO_OPENGL
+    GL_TEXTURE1_ARB,                //MUX_TEXEL1,
+    GL_CONSTANT_ARB,                //MUX_PRIM,
+    GL_PRIMARY_COLOR_ARB,           //MUX_SHADE,
+    GL_TEXTURE2_ARB,                //MUX_ENV,
+    //{GL_TEXTURE1_ARB,         },  //MUX_ENV,
+    GL_PREVIOUS_ARB,                //MUX_COMBALPHA,
+    GL_TEXTURE0_ARB,                //MUX_T0_ALPHA,
+    GL_TEXTURE1_ARB,                //MUX_T1_ALPHA,
+    GL_CONSTANT_ARB,                //MUX_PRIM_ALPHA,
+    GL_PRIMARY_COLOR_ARB,           //MUX_SHADE_ALPHA,
+    GL_TEXTURE2_ARB,                //MUX_ENV_ALPHA,
+    //{GL_TEXTURE1_ARB,         },  //MUX_ENV_ALPHA,
+    //{GL_TEXTURE3_ARB,         },  //MUX_LODFRAC,
+    //{GL_TEXTURE3_ARB,         },  //MUX_PRIMLODFRAC,
+    GL_TEXTURE1_ARB,                //MUX_LODFRAC,
+        GL_TEXTURE1_ARB,                //MUX_PRIMLODFRAC,
+    GL_PRIMARY_COLOR_ARB,           //MUX_K5
+    GL_PRIMARY_COLOR_ARB            //MUX_UNK
+#endif
+};
+
+GLint COGLColorCombiner2::RGBArgsMap2[] =
+{
+#if SDL_VIDEO_OPENGL
+    GL_PRIMARY_COLOR_ARB,           //MUX_0
+    GL_PRIMARY_COLOR_ARB,           //MUX_1
+    GL_PREVIOUS_ARB,                //MUX_COMBINED,
+    //{GL_TEXTURE,              },  //MUX_TEXEL0,
+    //{GL_TEXTURE,              },  //MUX_TEXEL1,
+#endif
+    GL_TEXTURE0_ARB,                //MUX_TEXEL0,
+#if SDL_VIDEO_OPENGL
+    GL_TEXTURE1_ARB,                //MUX_TEXEL1,
+    GL_CONSTANT_ARB,                //MUX_PRIM,
+    GL_PRIMARY_COLOR_ARB,           //MUX_SHADE,
+    GL_CONSTANT_ARB,                //MUX_ENV,
+    GL_PREVIOUS_ARB,                //MUX_COMBALPHA,
+    //{GL_TEXTURE,              },  //MUX_T0_ALPHA,
+    //{GL_TEXTURE,              },  //MUX_T1_ALPHA,
+    GL_TEXTURE0_ARB,                //MUX_TEXEL0,
+    GL_TEXTURE1_ARB,                //MUX_TEXEL1,
+    GL_CONSTANT_ARB,                //MUX_PRIM_ALPHA,
+    GL_PRIMARY_COLOR_ARB,           //MUX_SHADE_ALPHA,
+    GL_CONSTANT_ARB,                //MUX_ENV_ALPHA,
+    GL_CONSTANT_ARB,                //MUX_LODFRAC,
+    GL_CONSTANT_ARB,                //MUX_PRIMLODFRAC,
+    GL_PRIMARY_COLOR_ARB,           //MUX_K5
+    GL_PRIMARY_COLOR_ARB            //MUX_UNK
+#endif
+};
+
+//========================================================================
+
+GLint COGLColorCombiner4::MapRGBArgs(uint8 arg)
+{
+    return RGBArgsMap4[arg&MUX_MASK];
+}
+
+GLint COGLColorCombiner4v2::MapRGBArgs(uint8 arg)
+{
+    return RGBArgsMap4v2[arg&MUX_MASK];
+}
+
+GLint COGLColorCombiner2::MapRGBArgs(uint8 arg)
+{
+    return RGBArgsMap2[arg&MUX_MASK];
+}
+
+GLint COGLColorCombiner4::MapRGBArgFlags(uint8 arg)
+{
+    if( (arg & MUX_ALPHAREPLICATE) && (arg & MUX_COMPLEMENT) )
+    {
+        return GL_ONE_MINUS_SRC_ALPHA;
+    }
+    else if( (arg & MUX_ALPHAREPLICATE) )
+    {
+        return GL_SRC_ALPHA;
+    }
+    else if(arg & MUX_COMPLEMENT) 
+    {
+        return GL_ONE_MINUS_SRC_COLOR;
+    }
+    else
+        return GL_SRC_COLOR;
+}
+
+GLint COGLColorCombiner4::MapAlphaArgs(uint8 arg)
+{
+    return RGBArgsMap4[arg&MUX_MASK];
+}
+
+GLint COGLColorCombiner4v2::MapAlphaArgs(uint8 arg)
+{
+    return RGBArgsMap4v2[arg&MUX_MASK];
+}
+
+GLint COGLColorCombiner2::MapAlphaArgs(uint8 arg)
+{
+    return RGBArgsMap2[arg&MUX_MASK];
+}
+
+GLint COGLColorCombiner4::MapAlphaArgFlags(uint8 arg)
+{
+    if(arg & MUX_COMPLEMENT) 
+    {
+        return GL_ONE_MINUS_SRC_ALPHA;
+    }
+    else
+        return GL_SRC_ALPHA;
+}
+
+//========================================================================
+
+void ApplyFor1Unit(OGLExtCombinerType &unit)
+{
+#if SDL_VIDEO_OPENGL
+    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, unit.rgbOp);
+    OPENGL_CHECK_ERRORS;
+
+    if( unit.rgbArg0 != CM_IGNORE_BYTE )
+    {
+        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, (unit.rgbArg0gl));
+        OPENGL_CHECK_ERRORS;
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, (unit.rgbFlag0gl));
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if( unit.rgbArg1 != CM_IGNORE_BYTE )
+    {
+        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, (unit.rgbArg1gl));
+        OPENGL_CHECK_ERRORS;
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, (unit.rgbFlag1gl));
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if( unit.rgbArg2 != CM_IGNORE_BYTE )
+    {
+        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, (unit.rgbArg2gl));
+        OPENGL_CHECK_ERRORS;
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, (unit.rgbFlag2gl));
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if( unit.alphaArg0 != CM_IGNORE_BYTE )
+    {
+        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, (unit.alphaArg0gl));
+        OPENGL_CHECK_ERRORS;
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, (unit.alphaFlag0gl));
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if( unit.alphaArg1 != CM_IGNORE_BYTE )
+    {
+        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, (unit.alphaArg1gl));
+        OPENGL_CHECK_ERRORS;
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, (unit.alphaFlag1gl));
+        OPENGL_CHECK_ERRORS;
+    }
+
+    if( unit.alphaArg2 != CM_IGNORE_BYTE )
+    {
+        glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, (unit.alphaArg2gl));
+        OPENGL_CHECK_ERRORS;
+        glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, (unit.alphaFlag2gl));
+        OPENGL_CHECK_ERRORS;
+    }
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void COGLColorCombiner4::GenerateCombinerSetting(int index)
+{
+    OGLExtCombinerSaveType &res = m_vCompiledSettings[index];
+
+    // Texture unit 0
+    COGLTexture* pTexture = NULL;
+    COGLTexture* pTexture1 = NULL;
+
+    if( m_bTex0Enabled || m_bTex1Enabled || gRDP.otherMode.cycle_type  == CYCLE_TYPE_COPY )
+    {
+        if( m_bTex0Enabled || gRDP.otherMode.cycle_type  == CYCLE_TYPE_COPY )
+        {
+            pTexture = g_textures[gRSP.curTile].m_pCOGLTexture;
+            if( pTexture )  m_pOGLRender->BindTexture(pTexture->m_dwTextureName, 0);
+        }
+        if( m_bTex1Enabled )
+        {
+            pTexture1 = g_textures[(gRSP.curTile+1)&7].m_pCOGLTexture;
+            if( pTexture1 ) m_pOGLRender->BindTexture(pTexture1->m_dwTextureName, 1);
+        }
+    }
+
+
+
+    for( int i=0; i<res.numOfUnits; i++ )
+    {
+        pglActiveTexture(GL_TEXTURE0_ARB+i);
+        OPENGL_CHECK_ERRORS;
+        m_pOGLRender->EnableTexUnit(i,TRUE);
+        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+        OPENGL_CHECK_ERRORS;
+        ApplyFor1Unit(res.units[i]);
+    }
+
+    if( res.numOfUnits < m_maxTexUnits )
+    {
+        for( int i=res.numOfUnits; i<m_maxTexUnits; i++ )
+        {
+            pglActiveTexture(GL_TEXTURE0_ARB+i);
+            OPENGL_CHECK_ERRORS;
+            m_pOGLRender->DisBindTexture(0, i);
+            m_pOGLRender->EnableTexUnit(i,FALSE);
+        }
+    }
+}
+
+
+void COGLColorCombiner4::GenerateCombinerSettingConstants(int index)
+{
+    OGLExtCombinerSaveType &res = m_vCompiledSettings[index];
+
+    float *fv;
+    float tempf[4];
+
+    bool isused = true;
+
+    if( res.primIsUsed )
+    {
+        fv = GetPrimitiveColorfv(); // CONSTANT COLOR
+    }
+    else if( res.envIsUsed )
+    {
+        fv = GetEnvColorfv();   // CONSTANT COLOR
+    }
+    else if( res.lodFracIsUsed )
+    {
+        float frac = gRDP.LODFrac / 255.0f;
+        tempf[0] = tempf[1] = tempf[2] = tempf[3] = frac;
+        fv = &tempf[0];
+    }
+    else
+    {
+        isused = false;
+    }
+
+    if( isused )
+    {
+        for( int i=0; i<res.numOfUnits; i++ )
+        {
+            pglActiveTexture(GL_TEXTURE0_ARB+i);
+            OPENGL_CHECK_ERRORS;
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,fv);
+            OPENGL_CHECK_ERRORS;
+        }
+    }
+}
+
+
+void COGLColorCombiner4v2::GenerateCombinerSettingConstants(int index)
+{
+    //COGLColorCombiner4::GenerateCombinerSettingConstants(index);
+    //return;
+
+    OGLExtCombinerSaveType &res = m_vCompiledSettings[index];
+    COGLExtRender *prender = (COGLExtRender *)m_pRender;
+
+    if( res.primIsUsed )
+    {
+        float *fv = GetPrimitiveColorfv();  // CONSTANT COLOR
+        for( int i=0; i<res.numOfUnits; i++ )
+        {
+            pglActiveTexture(GL_TEXTURE0_ARB+i);
+            OPENGL_CHECK_ERRORS;
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,fv);
+            OPENGL_CHECK_ERRORS;
+        }
+    }
+
+    if( res.envIsUsed )
+    {
+        // Set Texture unit 2 to ENV
+        pglActiveTexture(GL_TEXTURE2_ARB);
+        OPENGL_CHECK_ERRORS;
+        prender->EnableTexUnit(2,TRUE);
+        TxtrCacheEntry *pEntry = gTextureManager.GetConstantColorTexture(MUX_ENV);
+        prender->SetCurrentTexture( (gRSP.curTile+2)%7, pEntry->pTexture, 4, 4, pEntry);
+        prender->SetTexelRepeatFlags((gRSP.curTile+2)%7);
+    }
+
+    if( res.lodFracIsUsed)
+    {
+        int unit = 3;
+        if( !res.envIsUsed )
+            unit = 2;
+
+        // Set Texture unit 3 to LODFRAC
+        pglActiveTexture(GL_TEXTURE0_ARB+unit);
+        OPENGL_CHECK_ERRORS;
+        prender->EnableTexUnit(unit,TRUE);
+        TxtrCacheEntry *pEntry = gTextureManager.GetConstantColorTexture(MUX_LODFRAC);
+        prender->SetCurrentTexture( (gRSP.curTile+unit)%7, pEntry->pTexture, 4, 4, pEntry);
+        prender->SetTexelRepeatFlags((gRSP.curTile+unit)%7);
+    }
+    else
+    {
+        int unit = 3;
+        if( !res.envIsUsed )
+            unit = 2;
+
+        // Disable texture unit 3
+        pglActiveTexture(GL_TEXTURE0_ARB+unit);
+        OPENGL_CHECK_ERRORS;
+        prender->EnableTexUnit(unit,FALSE);
+        prender->SetTextureToTextureUnitMap(-1,unit);
+    }
+}
+
+
+GLenum GeneralToGLMaps[]=
+{
+    GL_REPLACE,             //CM_REPLACE,
+#if SDL_VIDEO_OPENGL
+    GL_MODULATE,            //CM_MODULATE,
+    GL_ADD,                 //CM_ADD,
+    GL_SUBTRACT_ARB,        //CM_SUBTRACT,
+    GL_INTERPOLATE_ARB,     //CM_INTERPOLATE,
+    GL_INTERPOLATE_ARB,     //CM_ADDSMOOTH,     
+    GL_INTERPOLATE_ARB,     //CM_BLENDCURRENTALPHA
+    GL_INTERPOLATE_ARB,     //CM_BLENDDIFFUSEALPHA
+    GL_INTERPOLATE_ARB,     //CM_BLENDFACTORALPHA,
+    GL_INTERPOLATE_ARB,     //CM_BLENDTEXTUREALPHA
+#endif
+    GL_MODULATE_ADD_ATI,    //CM_MULTIPLYADD,       
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+int COGLColorCombiner2::ParseDecodedMux()
+{
+    //return COGLColorCombiner4::ParseDecodedMux();
+
+    int generalCombinerIndex = CGeneralCombiner::FindCompiledMux();
+    if( generalCombinerIndex < 0 )      // Can not found
+    {
+        generalCombinerIndex = CGeneralCombiner::ParseDecodedMux();
+    }
+
+    GeneralCombinerInfo &generalRes = m_vCompiledCombinerStages[generalCombinerIndex];
+    OGLExtCombinerSaveType res;
+
+    // Convert generalRes to OGLExtCombinerSaveType
+    for( int unitNo=0; unitNo<generalRes.nStages; unitNo++ )
+    {
+        OGLExtCombinerType &unit = res.units[unitNo];
+        //OGLExt1CombType &colorComb = unit.Combs[0];
+        //OGLExt1CombType &alphaComb = unit.Combs[1];
+
+        unit.rgbArg0 = (uint8)generalRes.stages[unitNo].colorOp.Arg1;
+        unit.rgbArg1 = (uint8)generalRes.stages[unitNo].colorOp.Arg2;
+        unit.rgbArg2 = (uint8)generalRes.stages[unitNo].colorOp.Arg0;
+        unit.alphaArg0 = (uint8)generalRes.stages[unitNo].alphaOp.Arg1;
+        unit.alphaArg1 = (uint8)generalRes.stages[unitNo].alphaOp.Arg2;
+        unit.alphaArg2 = (uint8)generalRes.stages[unitNo].alphaOp.Arg0;
+
+        unit.rgbOp = GeneralToGLMaps[generalRes.stages[unitNo].colorOp.op];
+        if( unit.rgbOp == GL_MODULATE_ADD_ATI && !m_bTxtOpMulAdd )
+        {
+            if( (unit.rgbArg0&MUX_MASK) == (unit.rgbArg2&MUX_MASK) && (unit.rgbArg0&MUX_COMPLEMENT) )
+            {
+                unit.rgbOp = GL_ADD;
+                unit.rgbArg0 &= ~MUX_COMPLEMENT;
+            }
+            else
+            {
+                unit.rgbOp = GL_MODULATE;
+            }
+        }
+        unit.alphaOp = GeneralToGLMaps[generalRes.stages[unitNo].alphaOp.op];
+        if( unit.alphaOp == GL_MODULATE_ADD_ATI && !m_bTxtOpMulAdd )    
+        {
+            if( (unit.alphaArg0&MUX_MASK) == (unit.alphaArg2&MUX_MASK) && (unit.alphaArg0&MUX_COMPLEMENT) )
+            {
+                unit.alphaOp = GL_ADD;
+                unit.alphaArg0 &= ~MUX_COMPLEMENT;
+            }
+            else
+            {
+                unit.alphaOp = GL_MODULATE;
+            }
+        }
+
+        unit.tex = generalRes.stages[unitNo].dwTexture;
+        unit.textureIsUsed = generalRes.stages[unitNo].bTextureUsed;
+    }
+
+    res.numOfUnits = generalRes.nStages;
+    res.constantColor = generalRes.TFactor;
+    return SaveParsedResult(res);
+}
+
+
+void COGLColorCombiner2::GenerateCombinerSettingConstants(int index)
+{
+    OGLExtCombinerSaveType &res = m_vCompiledSettings[index];
+
+    bool isused = true;
+
+    float *fv;
+    float tempf[4];
+
+    if( res.primIsUsed )
+    {
+        fv = GetPrimitiveColorfv(); // CONSTANT COLOR
+    }
+    else if( res.envIsUsed )
+    {
+        fv = GetEnvColorfv();   // CONSTANT COLOR
+    }
+    else if( res.lodFracIsUsed )
+    {
+        float frac = gRDP.LODFrac / 255.0f;
+        tempf[0] = tempf[1] = tempf[2] = tempf[3] = frac;
+        fv = &tempf[0];
+    }
+    else
+    {
+        isused = false;
+    }
+
+    if( isused )
+    {
+        for( int i=0; i<res.numOfUnits; i++ )
+        {
+            pglActiveTextureARB(GL_TEXTURE0_ARB+i);
+            OPENGL_CHECK_ERRORS;
+            glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,fv);
+            OPENGL_CHECK_ERRORS;
+        }
+    }
+}
+void COGLColorCombiner2::GenerateCombinerSetting(int index)
+{
+    OGLExtCombinerSaveType &res = m_vCompiledSettings[index];
+    COGLExtRender *prender = (COGLExtRender *)m_pRender;
+
+    for( int i=0; i<res.numOfUnits; i++ )
+    {
+        pglActiveTextureARB(GL_TEXTURE0_ARB+i);
+        OPENGL_CHECK_ERRORS;
+        //if(res.units[i].textureIsUsed)
+        {
+            prender->SetTextureToTextureUnitMap(res.units[i].tex,i);
+            m_pOGLRender->EnableTexUnit(i,TRUE);
+            COGLTexture* pTexture = g_textures[(gRSP.curTile+res.units[i].tex)&7].m_pCOGLTexture;
+            if( pTexture )  m_pOGLRender->BindTexture(pTexture->m_dwTextureName, i);
+        }
+        /*
+        else
+        {
+            m_pOGLRender->EnableTexUnit(i,TRUE);
+            prender->SetTextureToTextureUnitMap(-1,i);
+            //m_pOGLRender->EnableTexUnit(i,FALSE);
+            //m_pOGLRender->DisBindTexture(0, i);
+        }
+        */
+
+        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+        OPENGL_CHECK_ERRORS;
+        ApplyFor1Unit(res.units[i]);
+    }
+
+    if( res.numOfUnits < m_maxTexUnits )
+    {
+        for( int i=res.numOfUnits; i<m_maxTexUnits; i++ )
+        {
+            pglActiveTextureARB(GL_TEXTURE0_ARB+i);
+            OPENGL_CHECK_ERRORS;
+            m_pOGLRender->EnableTexUnit(i,FALSE);
+            prender->SetTextureToTextureUnitMap(-1,i);
+        }
+    }
+}
+