Here is a very old wrapper I made up years ago. The relevant section of the spec is in the comments. Not even sure if it still works but I hope it’s of some use to you. I would urge you to consider using shaders instead, as practically all hardware is capable of it these days.
// ------------------------------------------------------------------------------------------------
// EnvCombine
// - See the 2.1 spec, § 3.8.13, p. 184
// ------------------------------------------------------------------------------------------------
struct EnvCombine4
{
GLenum SrcFactor;
GLenum DestFactor;
bool UsePrimary;
EnvCombine4(GLenum srcFactor, GLenum destFactor, bool usePrimary)
: SrcFactor(srcFactor)
, DestFactor(destFactor)
, UsePrimary(usePrimary)
{}
void Apply(int texUnit)
{
glActiveTexture(GL_TEXTURE0 + texUnit);
if (texUnit == 0)
{
// No previous unit, so blend with framebuffer
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBlendFunc(SrcFactor, DestFactor);
}
else
{
// Combine BLEND : A * B + (1 - A) * C
// Combine4 ADD : A * B + C * D
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
// Arg0
GLenum srcColor = UsePrimary ? GL_PRIMARY_COLOR_EXT : GL_TEXTURE;
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB_EXT, GL_SRC_COLOR);
// Arg1
switch (SrcFactor)
{
case GL_ZERO:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_ZERO);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
break;
case GL_ONE:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_ZERO);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
break;
case GL_SRC_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
break;
case GL_ONE_MINUS_SRC_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
break;
case GL_SRC_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_ALPHA);
break;
case GL_ONE_MINUS_SRC_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_ALPHA);
break;
case GL_DST_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_COLOR);
break;
case GL_ONE_MINUS_DST_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_COLOR);
break;
case GL_DST_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_SRC_ALPHA);
break;
case GL_ONE_MINUS_DST_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND1_RGB_EXT, GL_ONE_MINUS_SRC_ALPHA);
break;
}
// Arg2
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE2_RGB_EXT, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND2_RGB_EXT, GL_SRC_COLOR);
// Arg3
switch (DestFactor)
{
case GL_ZERO:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_ZERO);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_COLOR);
break;
case GL_ONE:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_ZERO);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR);
break;
case GL_SRC_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_COLOR);
break;
case GL_ONE_MINUS_SRC_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR);
break;
case GL_SRC_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_ALPHA);
break;
case GL_ONE_MINUS_SRC_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, srcColor);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_ALPHA);
break;
case GL_DST_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_COLOR);
break;
case GL_ONE_MINUS_DST_COLOR:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_COLOR);
break;
case GL_DST_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_SRC_ALPHA);
break;
case GL_ONE_MINUS_DST_ALPHA:
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE3_RGB_NV, GL_PREVIOUS_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND3_RGB_NV, GL_ONE_MINUS_SRC_ALPHA);
break;
}
}
}
};