Arachnoid GLESv1.1 plugin. Compile and run (a bit glitchy and no frameskip) on the...
[mupen64plus-pandora.git] / source / mupen64plus-video-arachnoid / src / Combiner / AdvancedTexEnvCombiner.cpp
diff --git a/source/mupen64plus-video-arachnoid/src/Combiner/AdvancedTexEnvCombiner.cpp b/source/mupen64plus-video-arachnoid/src/Combiner/AdvancedTexEnvCombiner.cpp
new file mode 100755 (executable)
index 0000000..5d32a5d
--- /dev/null
@@ -0,0 +1,590 @@
+/******************************************************************************
+ * Arachnoid Graphics Plugin for Mupen64Plus
+ * http://bitbucket.org/wahrhaft/mupen64plus-video-arachnoid/
+ *
+ * Copyright (C) 2007 Kristofer Karlsson, Rickard Niklasson
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *****************************************************************************/
+
+#include <algorithm>
+using std::max;
+#include "AdvancedTexEnvCombiner.h"
+#include "CombinerStructs.h"
+#include "MultiTexturingExt.h"    //glActiveTextureARB
+#include "ExtensionChecker.h"
+
+#ifndef GL_ATI_texture_env_combine3
+#define GL_ATI_texture_env_combine3
+    #define GL_ATI_texture_env_combine3
+    #define GL_MODULATE_ADD_ATI                             0x8744
+    #define GL_MODULATE_SIGNED_ADD_ATI                      0x8745
+    #define GL_MODULATE_SUBTRACT_ATI                        0x8746
+#endif //GL_ATI_texture_env_combine3 
+
+#ifndef GL_ATIX_texture_env_route
+#define GL_ATIX_texture_env_route 1
+    #define GL_SECONDARY_COLOR_ATIX                 0x8747
+    #define GL_TEXTURE_OUTPUT_RGB_ATIX              0x8748
+    #define GL_TEXTURE_OUTPUT_ALPHA_ATIX            0x8749
+#endif // GL_ATIX_texture_env_route 
+
+static TexEnvCombinerArg TexEnvArgs[] =
+{
+    // CMB
+    { GL_PREVIOUS_ARB,       GL_SRC_COLOR },
+    // T0
+    { GL_TEXTURE,            GL_SRC_COLOR },
+    // T1
+    { GL_TEXTURE,            GL_SRC_COLOR },
+    // PRIM
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // SHADE
+    { GL_PRIMARY_COLOR_ARB,  GL_SRC_COLOR },
+    // ENV
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // CENTER
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // SCALE
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // CMBALPHA
+    { GL_PREVIOUS_ARB,       GL_SRC_ALPHA },
+    // T0ALPHA
+    { GL_TEXTURE,            GL_SRC_ALPHA },
+    // T1ALPHA
+    { GL_TEXTURE,            GL_SRC_ALPHA },
+    // PRIMALPHA
+    { GL_CONSTANT_ARB,       GL_SRC_ALPHA },
+    // SHADEALPHA
+    { GL_PRIMARY_COLOR_ARB,  GL_SRC_ALPHA },
+    // ENVALPHA
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // LODFRAC
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // PRIMLODFRAC
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // NOISE
+    { GL_TEXTURE,            GL_SRC_COLOR },
+    // K4
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // K5
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // ONE
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR },
+    // ZERO
+    { GL_CONSTANT_ARB,       GL_SRC_COLOR }
+};
+
+//-----------------------------------------------------------------------------
+//! Constructor
+//-----------------------------------------------------------------------------
+AdvancedTexEnvCombiner::AdvancedTexEnvCombiner()
+{
+}
+
+//-----------------------------------------------------------------------------
+//! Destructor
+//-----------------------------------------------------------------------------
+AdvancedTexEnvCombiner::~AdvancedTexEnvCombiner()
+{
+}
+
+//-----------------------------------------------------------------------------
+//* Initialize
+//! Checks if extensions are supported
+//-----------------------------------------------------------------------------
+void AdvancedTexEnvCombiner::initialize()
+{
+#ifdef HAVE_GLES
+    ARB_texture_env_combine  = true;
+    ARB_texture_env_crossbar = true;
+#else
+    ARB_texture_env_combine  = isExtensionSupported( "GL_ARB_texture_env_combine" );
+    ARB_texture_env_crossbar = isExtensionSupported( "GL_ARB_texture_env_crossbar" );
+#endif
+    ATI_texture_env_combine3 = isExtensionSupported( "GL_ATI_texture_env_combine3" );
+    ATIX_texture_env_route   = isExtensionSupported( "GL_ATIX_texture_env_route" );
+    NV_texture_env_combine4  = isExtensionSupported( "GL_NV_texture_env_combine4" );;
+
+    if ( ARB_texture_env_crossbar || NV_texture_env_combine4 || ATIX_texture_env_route )
+    {
+        TexEnvArgs[TEXEL0].source       = GL_TEXTURE0_ARB;
+        TexEnvArgs[TEXEL0_ALPHA].source = GL_TEXTURE0_ARB;
+        TexEnvArgs[TEXEL1].source       = GL_TEXTURE1_ARB;
+        TexEnvArgs[TEXEL1_ALPHA].source = GL_TEXTURE1_ARB;
+    }
+
+    if ( ATI_texture_env_combine3 )
+    {
+        TexEnvArgs[ONE].source  = GL_ONE;
+        TexEnvArgs[ZERO].source = GL_ZERO;
+    }
+}
+
+//-----------------------------------------------------------------------------
+//* Begin Texture Update
+//! Disables all textures
+//-----------------------------------------------------------------------------
+void AdvancedTexEnvCombiner::beginTextureUpdate()
+{
+    const int openGLMaxTextureUnits = 8;
+
+    //Disable all texture channels
+    for (int i = 0; i <openGLMaxTextureUnits; i++)
+    {
+        glActiveTextureARB(GL_TEXTURE0_ARB + i);
+        glDisable(GL_TEXTURE_2D );
+    }
+}
+
+
+//-----------------------------------------------------------------------------
+//* End Texture Update
+//! Enables selected textures in a texture environment
+//! @param texEnv Texture environment with textures to be enabled
+//-----------------------------------------------------------------------------
+void AdvancedTexEnvCombiner::endTextureUpdate(TexEnvCombiner* texEnv)
+{  
+    //Enable texturing
+    for (int i = 0; i <texEnv->usedUnits; i++)
+    {
+        glActiveTextureARB( GL_TEXTURE0_ARB + i );
+        glEnable( GL_TEXTURE_2D );
+    }
+}
+
+//-----------------------------------------------------------------------------
+//* Set Texture Enviroment Colors
+//! Sets texture enviorment colors for all active textures
+//! @param texEnv Texture environment with textures that will be assigned colors
+//-----------------------------------------------------------------------------
+void AdvancedTexEnvCombiner::setTextureEnviromentColors(TexEnvCombiner* texEnv)
+{
+    float color[4];
+
+    int openGLMaxTextureUnits = 8;
+
+    for (int i = 0; i < openGLMaxTextureUnits; i++)
+    {
+        this->getCombinerColor( color, texEnv->color[i].constant, texEnv->alpha[i].constant );
+
+        glActiveTextureARB(GL_TEXTURE0_ARB + i);
+        glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &color[0]);
+    }
+}
+
+//-----------------------------------------------------------------------------
+//* Set Texture Enviroment
+//! Sets texture environment for all active textures
+//! @param texEnv Texture environment with input data which will be 
+//!               sent to OpenGL using function "glTexEnvi" using ARB extensions
+//-----------------------------------------------------------------------------
+void AdvancedTexEnvCombiner::setTextureEnviroment(TexEnvCombiner* texEnv)
+{
+    const int openGLMaxTextureUnits = 8;
+
+    for (int i=0; i<openGLMaxTextureUnits; ++i)
+    {
+        glActiveTextureARB(GL_TEXTURE0_ARB + i);
+
+        if ( (i < texEnv->usedUnits ) || (i < 2 && texEnv->usesT1) )
+        {
+            glEnable( GL_TEXTURE_2D );
+            glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,   GL_COMBINE_ARB );
+            glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB,    texEnv->color[i].combine );
+            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB,    texEnv->color[i].arg0.source );
+            glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB,   texEnv->color[i].arg0.operand );
+            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB,    texEnv->color[i].arg1.source );
+            glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB,   texEnv->color[i].arg1.operand );
+            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB,    texEnv->color[i].arg2.source );
+            glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB,   texEnv->color[i].arg2.operand );
+            glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB,  texEnv->alpha[i].combine );
+            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB,  texEnv->alpha[i].arg0.source );
+            glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, texEnv->alpha[i].arg0.operand );
+            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB,  texEnv->alpha[i].arg1.source );
+            glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, texEnv->alpha[i].arg1.operand );
+            glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB,  texEnv->alpha[i].arg2.source );
+            glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, texEnv->alpha[i].arg2.operand );
+        }
+        else
+        {
+            glDisable(GL_TEXTURE_2D);
+        }            
+    }
+}
+
+//-----------------------------------------------------------------------------
+//* Create New Texture Enviornment
+//! Allocates a new texture enviroment
+//! @param[in] colorCombiner How to combine and get a color
+//! @param[in] alphaCombiner How to combine and get an alpha value
+//! @return The texture enviroment that was created
+//-----------------------------------------------------------------------------
+TexEnvCombiner* AdvancedTexEnvCombiner::createNewTextureEnviroment(Combiner* colorCombiner, Combiner *alphaCombiner)
+{
+    TexEnvCombiner* envCombiner = new TexEnvCombiner();
+
+    int curUnit;
+
+    const int openGLMaxTextureUnits = 8;
+
+    //For each texture unit
+    for (int i = 0; i < openGLMaxTextureUnits; i++)
+    {
+        envCombiner->color[i].combine = GL_REPLACE;
+        envCombiner->alpha[i].combine = GL_REPLACE;
+
+        SetColorCombinerValues( i, arg0, GL_PREVIOUS_ARB, GL_SRC_COLOR );
+        SetColorCombinerValues( i, arg1, GL_PREVIOUS_ARB, GL_SRC_COLOR );
+        SetColorCombinerValues( i, arg2, GL_PREVIOUS_ARB, GL_SRC_COLOR );
+        envCombiner->color[i].constant = COMBINED;
+        envCombiner->color[i].outputTexture = GL_TEXTURE0_ARB + i;
+
+        SetAlphaCombinerValues( i, arg0, GL_PREVIOUS_ARB, GL_SRC_ALPHA );
+        SetAlphaCombinerValues( i, arg1, GL_PREVIOUS_ARB, GL_SRC_ALPHA );
+        SetAlphaCombinerValues( i, arg2, GL_PREVIOUS_ARB, GL_SRC_ALPHA );
+        envCombiner->alpha[i].constant = COMBINED;
+        envCombiner->alpha[i].outputTexture = GL_TEXTURE0_ARB + i;
+    }
+
+    envCombiner->usesT0 = false;
+    envCombiner->usesT1 = false;
+
+    envCombiner->vertex.color = COMBINED;
+    envCombiner->vertex.secondaryColor = COMBINED;
+    envCombiner->vertex.alpha = COMBINED;
+
+    curUnit = 0;
+
+    for (int i=0; i<alphaCombiner->numStages; i++)
+    {
+        for (int j = 0; j < alphaCombiner->stage[i].numOps; j++)
+        {
+            float sb = 0.0f;
+
+            if (alphaCombiner->stage[i].op[j].param1 == PRIMITIVE_ALPHA)
+                sb = m_primColor[3];
+            else if (alphaCombiner->stage[i].op[j].param1 == ENV_ALPHA)
+                sb = m_envColor[3];
+            else if (alphaCombiner->stage[i].op[j].param1 == ONE)
+                sb = 1.0f;
+
+            if (((alphaCombiner->stage[i].numOps - j) >= 3) &&
+                (alphaCombiner->stage[i].op[j].op == SUB) &&
+                (alphaCombiner->stage[i].op[j+1].op == MUL) &&
+                (alphaCombiner->stage[i].op[j+2].op == ADD) &&
+                (sb > 0.5f) && 
+                (ARB_texture_env_combine))
+            {
+                envCombiner->usesT0 |= alphaCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA;
+                envCombiner->usesT1 |= alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA;
+
+                if (alphaCombiner->stage[i].op[j].param1 == ONE)
+                {
+                    SetAlphaCombinerValues( curUnit, arg0, envCombiner->alpha[curUnit].arg0.source, GL_ONE_MINUS_SRC_ALPHA );
+                }
+                else
+                {
+                    envCombiner->alpha[curUnit].combine = GL_SUBTRACT_ARB;
+                    SetAlphaCombinerValues( curUnit, arg1, envCombiner->alpha[curUnit].arg0.source, GL_SRC_ALPHA );
+                    SetAlphaCombinerArg( curUnit, arg0, alphaCombiner->stage[i].op[j].param1 );
+
+                    curUnit++;
+                }
+
+                j++;
+
+                envCombiner->usesT0 |= alphaCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA;
+                envCombiner->usesT1 |= alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA;
+
+                envCombiner->alpha[curUnit].combine = GL_MODULATE;
+                SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param1 );
+
+                curUnit++;
+                j++;
+
+                envCombiner->usesT0 |= alphaCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA;
+                envCombiner->usesT1 |= alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA;
+
+                envCombiner->alpha[curUnit].combine = GL_SUBTRACT_ARB;
+                SetAlphaCombinerArg( curUnit, arg0, alphaCombiner->stage[i].op[j].param1 );
+
+                curUnit++;
+            }
+            else
+            {
+                envCombiner->usesT0 |= alphaCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA;
+                envCombiner->usesT1 |= alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA;
+
+                switch (alphaCombiner->stage[i].op[j].op)
+                {
+                    case LOAD:
+                        if (!(ARB_texture_env_crossbar || NV_texture_env_combine4) &&
+                            (alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA) && (curUnit == 0))
+                            curUnit++;
+
+                        envCombiner->alpha[curUnit].combine = GL_REPLACE;
+
+                        SetAlphaCombinerArg( curUnit, arg0, alphaCombiner->stage[i].op[j].param1 );
+                        break;
+                    case SUB:
+                        if (!ARB_texture_env_combine)
+                            break;
+
+                        if (!(ARB_texture_env_crossbar || NV_texture_env_combine4) &&
+                            (alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA) && (curUnit == 0))
+                            curUnit++;
+
+                        if ((j > 0) && (alphaCombiner->stage[i].op[j-1].op == LOAD) && (alphaCombiner->stage[i].op[j-1].param1 == ONE))
+                        {
+                            SetAlphaCombinerArg( curUnit, arg0, alphaCombiner->stage[i].op[j].param1 );
+                            envCombiner->alpha[curUnit].arg0.operand = GL_ONE_MINUS_SRC_ALPHA;
+                        }
+                        else if ((ATI_texture_env_combine3) && (curUnit > 0) && (envCombiner->alpha[curUnit - 1].combine == GL_MODULATE))
+                        {
+                            curUnit--;
+                            SetAlphaCombinerValues( curUnit, arg2, envCombiner->alpha[curUnit].arg1.source, envCombiner->alpha[curUnit].arg1.operand );
+                            envCombiner->alpha[curUnit].combine = GL_MODULATE_SUBTRACT_ATI;
+                            SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param1 );
+                            curUnit++;
+                        }
+                        else
+                        {
+                            envCombiner->alpha[curUnit].combine = GL_SUBTRACT_ARB;
+                            SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param1 );
+                            curUnit++;
+                        }
+                        break;
+                    case MUL:
+                        if (!( ARB_texture_env_crossbar ||  NV_texture_env_combine4) &&
+                            (alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA) && (curUnit == 0))
+                            curUnit++;
+
+                        envCombiner->alpha[curUnit].combine = GL_MODULATE;
+
+                        SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param1 );
+                        curUnit++;
+                        break;
+                    case ADD:
+                        if (!(ARB_texture_env_crossbar || NV_texture_env_combine4) &&
+                            (alphaCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA) && (curUnit == 0))
+                            curUnit++;
+
+                        if ((ATI_texture_env_combine3) && (curUnit > 0) && (envCombiner->alpha[curUnit - 1].combine == GL_MODULATE))
+                        {
+                            curUnit--;
+                            SetAlphaCombinerValues( curUnit, arg2, envCombiner->alpha[curUnit].arg1.source, envCombiner->alpha[curUnit].arg1.operand );
+                            envCombiner->alpha[curUnit].combine = GL_MODULATE_ADD_ATI;
+                            SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param1 );
+                        }
+                        else
+                        {
+                            envCombiner->alpha[curUnit].combine = GL_ADD;
+                            SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param1 );
+                        }
+                        curUnit++;
+                        break;
+                    case INTERPOLATE:
+                        envCombiner->usesT0 |= (alphaCombiner->stage[i].op[j].param2 == TEXEL0_ALPHA) || (alphaCombiner->stage[i].op[j].param3 == TEXEL0_ALPHA);
+                        envCombiner->usesT1 |= (alphaCombiner->stage[i].op[j].param2 == TEXEL1_ALPHA) || (alphaCombiner->stage[i].op[j].param3 == TEXEL1_ALPHA);
+
+                        envCombiner->alpha[curUnit].combine = GL_INTERPOLATE_ARB;
+
+                        SetAlphaCombinerArg( curUnit, arg0, alphaCombiner->stage[i].op[j].param1 );
+                        SetAlphaCombinerArg( curUnit, arg1, alphaCombiner->stage[i].op[j].param2 );
+                        SetAlphaCombinerArg( curUnit, arg2, alphaCombiner->stage[i].op[j].param3 );
+
+                        curUnit++;
+                        break;
+                }
+            }
+        }
+    }
+
+    envCombiner->usedUnits = max( curUnit, 1 );
+
+    curUnit = 0;
+    for (int i = 0; i < colorCombiner->numStages; i++)
+    {
+        for (int j = 0; j < colorCombiner->stage[i].numOps; j++)
+        {
+            float sb = 0.0f;
+
+            if (colorCombiner->stage[i].op[j].param1 == PRIMITIVE)
+                sb = (m_primColor[0] + m_primColor[2] + m_primColor[1]) / 3.0f;
+            else if (colorCombiner->stage[i].op[j].param1 == ENVIRONMENT)
+                sb = (m_envColor[0] + m_envColor[2] + m_envColor[1]) / 3.0f;
+
+            // This helps with problems caused by not using signed values between texture units
+            if (((colorCombiner->stage[i].numOps - j) >= 3) &&
+                (colorCombiner->stage[i].op[j].op == SUB) &&
+                (colorCombiner->stage[i].op[j+1].op == MUL) &&
+                (colorCombiner->stage[i].op[j+2].op == ADD) &&
+                (sb > 0.5f) && 
+                (ARB_texture_env_combine))
+            {
+                envCombiner->usesT0 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL0) || (colorCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA));
+                envCombiner->usesT1 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA));
+
+                envCombiner->color[curUnit].combine = GL_SUBTRACT_ARB;
+                SetColorCombinerValues( curUnit, arg1, envCombiner->color[curUnit].arg0.source, envCombiner->color[curUnit].arg0.operand );
+                SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param1 );
+
+                curUnit++;
+                j++;
+
+                envCombiner->usesT0 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL0) || (colorCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA));
+                envCombiner->usesT1 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA));
+
+                envCombiner->color[curUnit].combine = GL_MODULATE;
+                SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param1 );
+
+                curUnit++;
+                j++;
+
+                envCombiner->usesT0 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL0) || (colorCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA));
+                envCombiner->usesT1 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA));
+
+                envCombiner->color[curUnit].combine = GL_SUBTRACT_ARB;
+                SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param1 );
+
+                curUnit++;
+            }
+            else
+            {
+                envCombiner->usesT0 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL0) || (colorCombiner->stage[i].op[j].param1 == TEXEL0_ALPHA));
+                envCombiner->usesT1 |= ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA));
+
+                switch (colorCombiner->stage[i].op[j].op)
+                {
+                    case LOAD:
+                        if (!( ARB_texture_env_crossbar ||  NV_texture_env_combine4) &&
+                            ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA)) && (curUnit == 0))
+                            curUnit++;
+
+                        envCombiner->color[curUnit].combine = GL_REPLACE;
+
+                        SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param1 );
+                        break;
+                    case SUB:
+                        if (!ARB_texture_env_combine)
+                            break;
+
+                        if (!(ARB_texture_env_crossbar ||  NV_texture_env_combine4) &&
+                            ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA)) && (curUnit == 0))
+                            curUnit++;
+
+                        if ((j > 0) && (colorCombiner->stage[i].op[j-1].op == LOAD) && (colorCombiner->stage[i].op[j-1].param1 == ONE))
+                        {
+                            SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param1 );
+                            envCombiner->color[curUnit].arg0.operand = GL_ONE_MINUS_SRC_COLOR;
+                        }
+                        else if (( ATI_texture_env_combine3) && (curUnit > 0) && (envCombiner->color[curUnit - 1].combine == GL_MODULATE))
+                        {
+                            curUnit--;
+                            SetColorCombinerValues( curUnit, arg2, envCombiner->color[curUnit].arg1.source, envCombiner->color[curUnit].arg1.operand );
+                            envCombiner->color[curUnit].combine = GL_MODULATE_SUBTRACT_ATI;
+                            SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param1 );
+                            curUnit++;
+                        }
+                        else
+                        {
+                            envCombiner->color[curUnit].combine = GL_SUBTRACT_ARB;
+                            SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param1 );
+                            curUnit++;
+                        }
+                        break;
+                    case MUL:
+                        if (!( ARB_texture_env_crossbar ||  NV_texture_env_combine4) &&
+                            ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA)) && (curUnit == 0))
+                            curUnit++;
+
+                        envCombiner->color[curUnit].combine = GL_MODULATE;
+
+                        SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param1 );
+                        curUnit++;
+                        break;
+                    case ADD:
+                        if (!( ARB_texture_env_crossbar ||  NV_texture_env_combine4) &&
+                            ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param1 == TEXEL1_ALPHA)) && (curUnit == 0))
+                            curUnit++;
+
+                        // ATI_texture_env_combine3 adds GL_MODULATE_ADD_ATI; saves texture units
+                        if (( ATI_texture_env_combine3) && (curUnit > 0) && (envCombiner->color[curUnit - 1].combine == GL_MODULATE))
+                        {
+                            curUnit--;
+                            SetColorCombinerValues( curUnit, arg2, envCombiner->color[curUnit].arg1.source, envCombiner->color[curUnit].arg1.operand );
+                            envCombiner->color[curUnit].combine = GL_MODULATE_ADD_ATI;
+                            SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param1 );
+                        }
+                        else
+                        {
+                            envCombiner->color[curUnit].combine = GL_ADD;
+                            SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param1 );
+                        }
+                        curUnit++;
+                        break;
+                    case INTERPOLATE:
+                        envCombiner->usesT0 |= (colorCombiner->stage[i].op[j].param2 == TEXEL0) || (colorCombiner->stage[i].op[j].param3 == TEXEL0) || (colorCombiner->stage[i].op[j].param3 == TEXEL0_ALPHA);
+                        envCombiner->usesT1 |= (colorCombiner->stage[i].op[j].param2 == TEXEL1) || (colorCombiner->stage[i].op[j].param3 == TEXEL1) || (colorCombiner->stage[i].op[j].param3 == TEXEL1_ALPHA);
+
+                        if (!( ARB_texture_env_crossbar ||  NV_texture_env_combine4) &&
+                            ((colorCombiner->stage[i].op[j].param1 == TEXEL1) || (colorCombiner->stage[i].op[j].param2 == TEXEL1) || (colorCombiner->stage[i].op[j].param3 == TEXEL1) || (colorCombiner->stage[i].op[j].param3 == TEXEL1_ALPHA)) && (curUnit == 0))
+                        {
+                            if (colorCombiner->stage[i].op[j].param1 == TEXEL0)
+                            {
+                                envCombiner->color[curUnit].combine = GL_REPLACE;
+                                SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param1 );
+                                colorCombiner->stage[i].op[j].param1 = COMBINED;
+                            }
+                            if (colorCombiner->stage[i].op[j].param2 == TEXEL0)
+                            {
+                                envCombiner->color[curUnit].combine = GL_REPLACE;
+                                SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param2 )
+
+                                colorCombiner->stage[i].op[j].param2 = COMBINED;
+                            }
+                            if (colorCombiner->stage[i].op[j].param3 == TEXEL0)
+                            {
+                                envCombiner->color[curUnit].combine = GL_REPLACE;
+                                SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param3 );
+                                colorCombiner->stage[i].op[j].param3 = COMBINED;
+                            }
+                            if (colorCombiner->stage[i].op[j].param3 == TEXEL0_ALPHA)
+                            {
+                                envCombiner->color[curUnit].combine = GL_REPLACE;
+                                SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param3 );
+                                colorCombiner->stage[i].op[j].param3 = COMBINED_ALPHA;
+                            }
+
+                            curUnit++;
+                        }
+
+                        envCombiner->color[curUnit].combine = GL_INTERPOLATE_ARB;
+
+                        SetColorCombinerArg( curUnit, arg0, colorCombiner->stage[i].op[j].param1 );
+                        SetColorCombinerArg( curUnit, arg1, colorCombiner->stage[i].op[j].param2 );
+                        SetColorCombinerArg( curUnit, arg2, colorCombiner->stage[i].op[j].param3 );
+
+                        curUnit++;
+                        break;
+                }
+            }
+        }
+    }
+
+    envCombiner->usedUnits = max( (unsigned short)curUnit, envCombiner->usedUnits );
+    return envCombiner;
+}