/*
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 "stdafx.h"
#include "glh_genext.h"

//========================================================================
COGLColorCombinerTNT2::COGLColorCombinerTNT2(CRender *pRender)
:COGLColorCombiner4(pRender)
{
	m_bTNT2Supported=false;
	delete m_pDecodedMux;
	m_pDecodedMux = new COGLDecodedMux;
	m_ppDecodedMux = &m_pDecodedMux;
}


bool COGLColorCombinerTNT2::Initialize(void)
{
	m_bTNT2Supported = false;

	if( COGLColorCombiner4::Initialize() )
	{
		m_bSupportMultiTexture = true;
		COGLGraphicsContext *pcontext = (COGLGraphicsContext *)(CGraphicsContext::g_pGraphicsContext);
		if( pcontext->IsExtensionSupported("GL_NV_texture_env_combine4") )
		{
			m_bTNT2Supported = true;
		}
		else
		{
			ErrorMsg("Your video card does not support OpenGL TNT2 extension combiner, you can only use the OpenGL Ext combiner functions");
		}
		return true;
	}
	return false;
}

//========================================================================

void COGLColorCombinerTNT2::InitCombinerCycle12(void)
{
	if( !m_bOGLExtCombinerSupported )	{ COGLColorCombiner4::InitCombinerCycle12(); return;}

	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 = CNvTNTCombiner::FindCompiledMux();
		if( m_lastIndex < 0 )		// Can not found
		{
			m_lastIndex = m_lastIndex = CNvTNTCombiner::ParseDecodedMux();
		}
		m_dwLastMux0 = m_pDecodedMux->m_dwMux0;
		m_dwLastMux1 = m_pDecodedMux->m_dwMux1;
	}

	m_pOGLRender->SetAllTexelRepeatFlag();

	if( m_bCycleChanged || combinerIsChanged || gRDP.texturesAreReloaded )
	{
		gRDP.texturesAreReloaded = false;

		if( m_bCycleChanged || combinerIsChanged )
		{
			GenerateCombinerSettingConstants(m_lastIndex);
			GenerateCombinerSetting(m_lastIndex);
		}
	}
}

const char* COGLColorCombinerTNT2::GetOpStr(GLenum op)
{
	switch( op )
	{
	case GL_ADD:
		return "MOD";
	default:
		return "ADD_SIGNED";
	}
}

//========================================================================

GLint COGLColorCombinerTNT2::RGBArgsMap[] =
{
	{GL_ZERO,					},	//MUX_0
	{GL_ZERO,					},	//MUX_1
	{GL_PREVIOUS_EXT,			},	//MUX_COMBINED,
	{GL_TEXTURE0_ARB,			},	//MUX_TEXEL0,
	{GL_TEXTURE1_ARB,			},	//MUX_TEXEL1,
	{GL_CONSTANT_EXT,			},	//MUX_PRIM,
	{GL_PRIMARY_COLOR_EXT,		},	//MUX_SHADE,
	{GL_CONSTANT_EXT,			},	//MUX_ENV,
	{GL_PREVIOUS_EXT,			},	//MUX_COMBALPHA,
	{GL_TEXTURE0_ARB,			},	//MUX_T0_ALPHA,
	{GL_TEXTURE1_ARB,			},	//MUX_T1_ALPHA,
	{GL_CONSTANT_EXT,			},	//MUX_PRIM_ALPHA,
	{GL_PRIMARY_COLOR_EXT,		},	//MUX_SHADE_ALPHA,
	{GL_CONSTANT_EXT,			},	//MUX_ENV_ALPHA,
	{GL_CONSTANT_EXT,			},	//MUX_LODFRAC,
	{GL_CONSTANT_EXT,			},	//MUX_PRIMLODFRAC,
	{GL_ZERO,					},	//MUX_K5
	{GL_ZERO,					},	//MUX_UNK
};


//========================================================================

GLint COGLColorCombinerTNT2::MapRGBArgs(uint8 arg)
{
	return RGBArgsMap[arg&MUX_MASK];
}

GLint COGLColorCombinerTNT2::MapRGBArgFlags(uint8 arg)
{
	if( (arg & MUX_ALPHAREPLICATE) && (arg & MUX_COMPLEMENT) )
	{
		return GL_ONE_MINUS_SRC_ALPHA;
	}
	else if( (arg & MUX_ALPHAREPLICATE) )
	{
		if( arg == MUX_1 )
			return GL_ONE_MINUS_SRC_ALPHA;
		else
			return GL_SRC_ALPHA;
	}
	else if(arg & MUX_COMPLEMENT || arg == MUX_1) 
	{
		return GL_ONE_MINUS_SRC_COLOR;
	}
	else
		return GL_SRC_COLOR;
}

GLint COGLColorCombinerTNT2::MapAlphaArgs(uint8 arg)
{
	return RGBArgsMap[arg&MUX_MASK];
}

GLint COGLColorCombinerTNT2::MapAlphaArgFlags(uint8 arg)
{
	if(arg & MUX_COMPLEMENT || arg == MUX_1) 
	{
		return GL_ONE_MINUS_SRC_ALPHA;
	}
	else
		return GL_SRC_ALPHA;
}

//========================================================================

void COGLColorCombinerTNT2::GenerateCombinerSetting(int index)
{
	TNT2CombinerSaveType &res = m_vCompiledTNTSettings[index];

	// Texture unit 0
	COGLTexture* pTexture = g_textures[gRSP.curTile].m_pCOGLTexture;
	COGLTexture* pTexture1 = g_textures[(gRSP.curTile+1)&7].m_pCOGLTexture;

	if( pTexture )	m_pOGLRender->BindTexture(pTexture->m_dwTextureName, 0);
	if( pTexture1 )	m_pOGLRender->BindTexture(pTexture1->m_dwTextureName, 1);

	// Texture unit 0
	glActiveTextureARB(GL_TEXTURE0_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV);
	m_pOGLRender->EnableTexUnit(0,TRUE);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, res.unit1.rgbOp);
	glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, res.unit1.alphaOp);

	if( res.unit1.rgbOp == GL_SUBTRACT_ARB )
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, MapRGBArgs(res.unit1.rgbArg0^MUX_COMPLEMENT));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, MapRGBArgFlags(res.unit1.rgbArg0^MUX_COMPLEMENT));
	}
	else
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, MapRGBArgs(res.unit1.rgbArg0));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, MapRGBArgFlags(res.unit1.rgbArg0));
	}

	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, MapRGBArgs(res.unit1.rgbArg1));
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, MapRGBArgFlags(res.unit1.rgbArg1));

	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, MapRGBArgs(res.unit1.rgbArg2));
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, MapRGBArgFlags(res.unit1.rgbArg2));

	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_RGB_EXT, MapRGBArgs(res.unit1.rgbArg3));
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_RGB_EXT, MapRGBArgFlags(res.unit1.rgbArg3));

	if( res.unit1.alphaOp == GL_SUBTRACT_ARB )
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, MapRGBArgs(res.unit1.alphaArg0^MUX_COMPLEMENT));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, MapAlphaArgFlags(res.unit1.alphaArg0^MUX_COMPLEMENT));
	}
	else
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, MapRGBArgs(res.unit1.alphaArg0));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, MapAlphaArgFlags(res.unit1.alphaArg0));
	}

	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, MapRGBArgs(res.unit1.alphaArg1));
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, MapAlphaArgFlags(res.unit1.alphaArg1));

	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, MapRGBArgs(res.unit1.alphaArg2));
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, MapAlphaArgFlags(res.unit1.alphaArg2));

	glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_EXT, MapRGBArgs(res.unit1.rgbArg3));
	glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_EXT, MapAlphaArgFlags(res.unit1.rgbArg3));

	glActiveTextureARB(GL_TEXTURE1_ARB);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE4_NV);

	if( m_maxTexUnits > 1 && res.numOfUnits > 1 )
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, res.unit2.rgbOp);
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, res.unit2.alphaOp);

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, MapRGBArgs(res.unit2.rgbArg0));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, MapRGBArgFlags(res.unit2.rgbArg0));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, MapRGBArgs(res.unit2.rgbArg1));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, MapRGBArgFlags(res.unit2.rgbArg1));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, MapRGBArgs(res.unit2.rgbArg2));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, MapRGBArgFlags(res.unit2.rgbArg2));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_RGB_EXT, MapRGBArgs(res.unit2.rgbArg3));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_RGB_EXT, MapRGBArgFlags(res.unit2.rgbArg3));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, MapRGBArgs(res.unit2.alphaArg0));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, MapAlphaArgFlags(res.unit2.alphaArg0));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, MapRGBArgs(res.unit2.alphaArg1));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, MapAlphaArgFlags(res.unit2.alphaArg1));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, MapRGBArgs(res.unit2.alphaArg2));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, MapAlphaArgFlags(res.unit2.alphaArg2));

		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE3_ALPHA_EXT, MapRGBArgs(res.unit2.alphaArg3));
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND3_ALPHA_EXT, MapAlphaArgFlags(res.unit2.alphaArg3));

		m_pOGLRender->EnableTexUnit(1,TRUE);
	}
	else
	{
		//m_pOGLRender->EnableTexUnit(1,FALSE);
	}
}

void COGLColorCombinerTNT2::GenerateCombinerSettingConstants(int index)
{
	TNT2CombinerSaveType &res = m_vCompiledTNTSettings[index];
	for( int i=0; i<2; i++ )
	{
		float *fv;
		glActiveTextureARB(GL_TEXTURE0_ARB+i);
		switch( res.units[i].constant & MUX_MASK )
		{
		case MUX_PRIM:
			fv = GetPrimitiveColorfv();
			glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,fv);
			break;
		case MUX_ENV:
			fv = GetEnvColorfv();
			glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,fv);
			break;
		case MUX_LODFRAC:
			{
				float frac = gRDP.LODFrac / 255.0f;
				float tempf[4] = {frac,frac,frac,frac};
				glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,tempf);
				break;
			}
		case MUX_PRIMLODFRAC:
			{
				float frac = gRDP.primLODFrac / 255.0f;
				float tempf[4] = {frac,frac,frac,frac};
				glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,tempf);
				break;
			}
		}
	}
}


