                                   mediump vec4 textureEnvColor,
                                         mediump vec4 vertexColor,
                                         mediump vec4 texturePrevColor,
                                         mediump vec4 textureColor)
{
    mediump float res;
    mediump vec4 op;

    switch (srcn)
    {
        case kTexture:
            op = textureColor;
            break;
        case kConstant:
            op = textureEnvColor;
            break;
        case kPrimaryColor:
            op = vertexColor;
            break;
        case kPrevious:
            op = texturePrevColor;
            break;
        default:
            op = texturePrevColor;
            break;
    }

    switch (opn)
    {
        case kSrcAlpha:
            res = op.a;
            break;
        case kOneMinusSrcAlpha:
            res = 1.0 - op.a;
            break;
        default:
            break;
    }

    return res;
}

mediump vec4 textureCombine(uint combineRgb,
                            uint combineAlpha,
                            uint src0Rgb,
                            uint src0Alpha,
                            uint src1Rgb,
                            uint src1Alpha,
                            uint src2Rgb,
                            uint src2Alpha,
                            uint op0Rgb,
                            uint op0Alpha,
                            uint op1Rgb,
                            uint op1Alpha,
                            uint op2Rgb,
                            uint op2Alpha,
                            mediump vec4 textureEnvColor,
                            mediump float rgbScale,
                            mediump float alphaScale,
                            mediump vec4 vertexColor,
                            mediump vec4 texturePrevColor,
                            mediump vec4 textureColor)
{

    mediump vec3 resRgb;
    mediump float resAlpha;

    mediump vec3 arg0Rgb;
    mediump float arg0Alpha;
    mediump vec3 arg1Rgb;
    mediump float arg1Alpha;
    mediump vec3 arg2Rgb;
    mediump float arg2Alpha;
    mediump float dotVal;

    arg0Rgb   = textureCombineSrcnOpnRgb(src0Rgb, op0Rgb, textureEnvColor, vertexColor,
                                       texturePrevColor, textureColor);
    arg0Alpha = textureCombineSrcnOpnAlpha(src0Alpha, op0Alpha, textureEnvColor, vertexColor,
                                           texturePrevColor, textureColor);

    if (combineRgb != kReplace)
    {
        arg1Rgb = textureCombineSrcnOpnRgb(src1Rgb, op1Rgb, textureEnvColor, vertexColor,
                                           texturePrevColor, textureColor);
    }

    if (combineAlpha != kReplace)
    {
        arg1Alpha = textureCombineSrcnOpnAlpha(src1Alpha, op1Alpha, textureEnvColor, vertexColor,
                                               texturePrevColor, textureColor);
    }

    if (combineRgb == kInterpolate)
    {
        arg2Rgb = textureCombineSrcnOpnRgb(src2Rgb, op2Rgb, textureEnvColor, vertexColor,
                                           texturePrevColor, textureColor);
    }

    if (combineAlpha == kInterpolate)
    {
        arg2Alpha = textureCombineSrcnOpnAlpha(src2Alpha, op2Alpha, textureEnvColor, vertexColor,
                                               texturePrevColor, textureColor);
    }

    switch (combineRgb)
    {
        case kReplace:
            resRgb = arg0Rgb;
            break;
        case kModulate:
            resRgb = arg0Rgb * arg1Rgb;
            break;
        case kAdd:
            resRgb = arg0Rgb + arg1Rgb;
            break;
        case kAddSigned:
            resRgb = arg0Rgb + arg1Rgb - 0.5;
            break;
        case kInterpolate:
            resRgb = arg0Rgb * arg2Rgb + arg1Rgb * (1.0 - arg2Rgb);
            break;
        case kSubtract:
            resRgb = arg0Rgb - arg1Rgb;
            break;
        default:
            break;
    }

    switch (combineAlpha)
    {
        case kReplace:
            resAlpha = arg0Alpha;
            break;
        case kModulate:
            resAlpha = arg0Alpha * arg1Alpha;
            break;
        case kAdd:
            resAlpha = arg0Alpha + arg1Alpha;
            break;
        case kAddSigned:
            resAlpha = arg0Alpha + arg1Alpha - 0.5;
            break;
        case kInterpolate:
            resAlpha = arg0Alpha * arg2Alpha + arg1Alpha * (1.0 - arg2Alpha);
            break;
        case kSubtract:
            resAlpha = arg0Alpha - arg1Alpha;
            break;
        default:
            break;
    }

    if (combineRgb == kDot3Rgb || combineRgb == kDot3Rgba)
    {
        dotVal = 4.0 * dot(arg0Rgb - 0.5, arg1Rgb - 0.5);

        if (combineRgb == kDot3Rgb)
        {
            return vec4(dotVal, dotVal, dotVal, resAlpha);
        }
        else
        {
            return vec4(dotVal, dotVal, dotVal, dotVal);
        }
    }
    else
    {
        return vec4(resRgb, resAlpha);
    }
}

mediump vec4 textureFunction(uint unit,
                             uint texFormat,
                             uint envMode,
                             uint combineRgb,
                             uint combineAlpha,
                             uint src0Rgb,
                             uint src0Alpha,
                             uint src1Rgb,
                             uint src1Alpha,
                             uint src2Rgb,
                             uint src2Alpha,
                             uint op0Rgb,
                             uint op0Alpha,
                             uint op1Rgb,
                             uint op1Alpha,
                             uint op2Rgb,
                             uint op2Alpha,
                             mediump vec4 textureEnvColor,
                             mediump float rgbScale,
                             mediump float alphaScale,
                             mediump vec4 vertexColor,
                             mediump vec4 texturePrevColor,
                             mediump vec4 textureColor)
{

    if (!isTextureUnitEnabled(unit))
    {
        return texturePrevColor;
    }

    mediump vec4 res;

    switch (envMode)
    {
        case kReplace:
            switch (texFormat)
            {
                case kAlpha:
                    res.rgb = texturePrevColor.rgb;
                    res.a   = textureColor.a;
                    break;
                case kRGBA:
                case kLuminanceAlpha:
                    res.rgba = textureColor.rgba;
                    break;
                case kRGB:
                case kLuminance:
                default:
                    res.rgb = textureColor.rgb;
                    res.a   = texturePrevColor.a;
                    break;
            }
            break;
        case kModulate:
            switch (texFormat)
            {
                case kAlpha:
                    res.rgb = texturePrevColor.rgb;
                    res.a   = texturePrevColor.a * textureColor.a;
                    break;
                case kRGBA:
                case kLuminanceAlpha:
                    res.rgba = texturePrevColor.rgba * textureColor.rgba;
                    break;
                case kRGB:
                case kLuminance:
                default:
                    res.rgb = texturePrevColor.rgb * textureColor.rgb;
                    res.a   = texturePrevColor.a;
                    break;
            }
            break;
        case kDecal:
            switch (texFormat)
            {
                case kRGB:
                    res.rgb = textureColor.rgb;
                    res.a   = texturePrevColor.a;
                    break;
                case kRGBA:
                    res.rgb = texturePrevColor.rgb * (1.0 - textureColor.a) +
                              textureColor.rgb * textureColor.a;
                    res.a = texturePrevColor.a;
                    break;
                case kAlpha:
                case kLuminance:
                case kLuminanceAlpha:
                default:
                    res.rgb = texturePrevColor.rgb * textureColor.rgb;
                    res.a   = texturePrevColor.a;
                    break;
            }
            break;
        case kBlend:
            switch (texFormat)
            {
                case kAlpha:
                    res.rgb = texturePrevColor.rgb;
                    res.a   = textureColor.a * texturePrevColor.a;
                    break;
                case kLuminance:
                case kRGB:
                    res.rgb = texturePrevColor.rgb * (1.0 - textureColor.rgb) +
                              textureEnvColor.rgb * textureColor.rgb;
                    res.a = texturePrevColor.a;
                    break;
                case kLuminanceAlpha:
                case kRGBA:
                default:
                    res.rgb = texturePrevColor.rgb * (1.0 - textureColor.rgb) +
                              textureEnvColor.rgb * textureColor.rgb;
                    res.a = textureColor.a * texturePrevColor.a;
                    break;
            }
            break;
        case kAdd:
            switch (texFormat)
            {
                case kAlpha:
                    res.rgb = texturePrevColor.rgb;
                    res.a   = textureColor.a * texturePrevColor.a;
                    break;
                case kLuminance:
                case kRGB:
                    res.rgb = texturePrevColor.rgb + textureColor.rgb;
                    res.a   = texturePrevColor.a;
                    break;
                case kLuminanceAlpha:
                case kRGBA:
                default:
                    res.rgb = texturePrevColor.rgb + textureColor.rgb;
                    res.a   = textureColor.a * texturePrevColor.a;
                    break;
            }
            break;
        case kCombine:
            res = textureCombine(combineRgb, combineAlpha, src0Rgb, src0Alpha, src1Rgb, src1Alpha,
                                 src2Rgb, src2Alpha, op0Rgb, op0Alpha, op1Rgb, op1Alpha, op2Rgb,
                                 op2Alpha, textureEnvColor, rgbScale, alphaScale, vertexColor,
                                 texturePrevColor, textureColor);
            res.rgb *= rgbScale;
            res.a *= alphaScale;
            break;
        default:
            break;
    }

    return clamp(res, 0.0, 1.0);
}
