//////////////////////////////////////////////////////////////////////
//
//                             Pixie
//
// Copyright © 1999 - 2003, Okan Arikan
//
// Contact: okan@cs.berkeley.edu
//
// This library 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 library 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 library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
//  File				:	execute.cpp
//  Classes				:	-
//  Description			:	This file implements the main shader execution
//
////////////////////////////////////////////////////////////////////////
#include <math.h>
#include <float.h>

#include "common/global.h"
#include "memory.h"
#include "output.h"
#include "renderer.h"
#include "shader.h"
#include "slcode.h"
#include "shading.h"
#include "noise.h"
#include "stats.h"
#include "photonMap.h"
#include "irradiance.h"
#include "bundles.h"
#include "colorMap.h"
#include "error.h"
#include "executeMisc.h"


///////////////////////////////////////////////////////////////////////
// Function				:	debugFunction
// Description			:	This function is used to debugging purposes
// Return Value			:	-
// Comments				:
// Date last edited		:	2/4/2003
void	debugFunction(float *op) {
	int	i	=	1;
}

// These functions are defined in init.cpp
void							convertColorFrom(float *,const float *,ECoordinateSystem);
void							convertColorTo(float *,const float *,ECoordinateSystem);

#define	shaderDiffusePoint(__r,__cLight) {											\
	const	CShadedLight	*tLight		=	__cLight;								\
																					\
	initv((float *) __r,0,0,0);														\
																					\
	for (;tLight!=NULL;tLight=tLight->next) {										\
		__r[COMP_R].real	+=	tLight->Cl[COMP_R];									\
		__r[COMP_G].real	+=	tLight->Cl[COMP_G];									\
		__r[COMP_B].real	+=	tLight->Cl[COMP_B];									\
	}																				\
}

#define	shaderDiffuse(__r,__N,__a,__cLight) {										\
	const	float			cosangle	=	(float) cos(__a);						\
	const	CShadedLight	*tLight		=	__cLight;								\
																					\
	initv((float *) __r,0,0,0);														\
																					\
	for (;tLight!=NULL;tLight=tLight->next) {										\
		const	float	coefficient	=	dotvv((const float *) __N,tLight->L);		\
																					\
		if (coefficient > cosangle) {												\
			__r[COMP_R].real	+=	coefficient*tLight->Cl[COMP_R];					\
			__r[COMP_G].real	+=	coefficient*tLight->Cl[COMP_G];					\
			__r[COMP_B].real	+=	coefficient*tLight->Cl[COMP_B];					\
		}																			\
	}																				\
}

#define	specularBRDF(__r,__L,__N,__V,__roughness) {									\
	vector	halfway;																\
																					\
	addvv(halfway,(const float *) __V,(const float *) __L);							\
	normalizev(halfway);															\
	__r[0].real	=	(float) pow(max(0,dotvv((const float *) __N,halfway)),((float) 10)/__roughness);	\
	__r[1].real	=	__r[0].real;													\
	__r[2].real	=	__r[1].real;													\
}


#define	shaderSpecular(__r,__N,__V,__roughness,__cLight) {							\
	const	CShadedLight	*tLight		=	__cLight;								\
	const	float			power		=	10 / __roughness;						\
	initv((float *) __r,0,0,0);														\
	for (;tLight != NULL;tLight=tLight->next) {										\
		vector	cColor;																\
		float	coefficient;														\
		vector	halfway;															\
		addvv(halfway,(const float *) __V,tLight->L);								\
		normalizev(halfway);														\
		coefficient	=	dotvv((const float *) __N,halfway);							\
		if (coefficient > 0) {														\
			coefficient	=	(float) pow(coefficient,power);							\
			mulvf(cColor,tLight->Cl,coefficient);									\
			addvv((float *) __r,cColor);											\
		}																			\
	}																				\
}


#define	shaderPhong(__r,__N,__V,__size,__cLight) {									\
	const	CShadedLight	*tLight		=	__cLight;								\
	vector	reflection;																\
																					\
	mulvf(reflection,(const float *) __N,2*dotvv((const float *) __N,(const float *) __V));	\
	subvv(reflection,(const float *) __V);											\
																					\
	initv((float *) __r,0,0,0);														\
	for (;tLight != NULL;tLight=tLight->next) {										\
		const	float	coefficient	=	(float) pow(max(0,dotvv(reflection,tLight->L)),__size);	\
																					\
		if (coefficient > 0) {														\
			__r[COMP_R].real	+=	coefficient*tLight->Cl[COMP_R];					\
			__r[COMP_G].real	+=	coefficient*tLight->Cl[COMP_G];					\
			__r[COMP_B].real	+=	coefficient*tLight->Cl[COMP_B];					\
		}																			\
	}																				\
}






///////////////////////////////////////////////////////////////////////
// Class				:	CShadingContext
// Method				:	execute
// Description			:	Execute a shader
// Return Value			:	-
// Comments				:
// Date last edited		:	11/28/2001
void	CShadingContext::execute(CProgrammableShaderInstance *cInstance) {
// At this point, the shader sends us the arrays for parameters/constants/variables/uniforms for the shader
	
//	The maximum allowed string size that can be handled
#define		MAX_SCRIPT_STRING_SIZE			256


#define		scripterror(mes)				{																	\
												error(CODE_SCRIPT,"\"%s\", (nullified)\n",mes);					\
												cInstance->parent->codeEntryPoint	=	-1;						\
												cInstance->parent->initEntryPoint	=	-1;						\
												goto	execEnd;												\
											}
//	Allocate temporary memory for the string and save it
#define		savestring(r,n)					{																	\
												int		strLen	=	strlen(n) + 1;								\
												int		strSize	=	(strLen & ~3) + 4;							\
												char	*strmem	=	(char *) ralloc(strSize);					\
												strcpy(strmem,n);												\
												r				=	strmem;										\
											}

//	Begin a conditional block execution
#define		beginConditional()				if (conditionals == NULL) {											\
												conditionals		=	(CConditional *) frameMemory->alloc(sizeof(CConditional));						\
												conditionals->next	=	NULL;									\
												conditionals->prev	=	NULL;									\
											}																	\
																												\
											conditionals->prev		=	lastConditional;						\
											lastConditional			=	conditionals;							\
											conditionals			=	lastConditional->next;
											

//	End a conditional block execution
#define		endConditional()				lastConditional->next	=	conditionals;							\
											conditionals			=	lastConditional;						\
											lastConditional			=	lastConditional->prev;

//	Retrieve a pointer to an operand and obtain it's size
#define		operand(i,n)					{																	\
												const TCode	ref	=	code[IP+i+2];								\
												n	= stuff[ref.reference.accessor][ref.reference.index];		\
											}

//	Retrieve an operand's size
#define		operandSize(i,n,s)				{																	\
												const TCode	ref	=	code[IP+i+2];								\
												n	= stuff[ref.reference.accessor][ref.reference.index];		\
												s	= ref.reference.numItems;									\
											}

// Retrieve the parameterlist
#define		parameterlist					cInstance->parameterLists[code[IP+1].arguments.plNumber]

#define		dirty()							if (cInstance->dirty == FALSE) {										\
												cInstance->dirty	=	TRUE;										\
												if (dirtyInstances == NULL)											\
													dirtyInstances	=	new CArray<CProgrammableShaderInstance *>;	\
												cInstance->attach();												\
												dirtyInstances->push(cInstance);									\
											}

//	Retrieve an integer operand (label references are integer)
#define		argument(i)						code[IP+i+2].integer;

//	Retrieve an integer operand (label references are integer)
#define		argumentCode(i)					code[IP+i+2]

//	Retrieve the number of arguments
#define		argumentcount(n)				n = code[IP+1].arguments.numArguments;

//	Control transfer
#define		jmp(n)							{																\
												IP				=	n;										\
												goto execStart;												\
											}

#define		hashLookupNegative(__dest)


//	Run the ambient light source shaders for the lP
#define		runAmbientLights()				if (!currentShadingState->ambientLightsExecuted) {				\
												const CAttributes		*currentAttributes	=	currentShadingState->currentObject->attributes;	\
												assert((numActive+numPassive) == numVertices);				\
												assert(numVertices == currentShadingState->numVertices);	\
												currentShadingState->numActive				=	numActive;	\
												currentShadingState->numPassive				=	numPassive;	\
												currentShadingState->ambientLightsExecuted	=	TRUE;		\
																											\
												if (inShadow == FALSE) {									\
													CActiveLight			*cLight;						\
																											\
													for (cLight=currentAttributes->lightSources;cLight!=NULL;cLight=cLight->next) {	\
														CLightInstance	*light	=	cLight->light;							\
														if (!(light->parent->usedParameters & PARAMETER_NONAMBIENT)) {		\
															currentShadingState->currentLightInstance	=	light;			\
															light->illuminate(this);										\
														}													\
													}														\
												}															\
												assert(currentShadingState->numActive	==	numActive);		\
												assert(currentShadingState->numPassive	==	numPassive);	\
																											\
												currentShadingState->currentShaderInstance	=	cInstance;	\
											}


//	Run the light source shaders for the lP
#define		runLights(lP,lN,lT)				if (!currentShadingState->lightsExecuted) {						\
												int			i;												\
												float		*Ps;											\
												const float	*point	=	lP;									\
												const CAttributes		*currentAttributes	=	currentShadingState->currentObject->attributes;	\
												Ps				=	varying[VARIABLE_PS];					\
												for (i=numVertices;i>0;i--) {								\
													movvv(Ps,point);										\
																											\
													Ps		+=	3;											\
													point	+=	3;											\
												}															\
												assert((numActive+numPassive) == numVertices);				\
												assert(numVertices == currentShadingState->numVertices);	\
												currentShadingState->numActive		=	numActive;			\
												currentShadingState->numPassive		=	numPassive;			\
												currentShadingState->lightsExecuted	=	TRUE;				\
												currentShadingState->Ns				=	lN;					\
												currentShadingState->costheta		=	lT;					\
																											\
												if (inShadow == FALSE) {									\
													CActiveLight			*cLight;						\
																											\
													for (cLight=currentAttributes->lightSources;cLight!=NULL;cLight=cLight->next) {	\
														currentShadingState->currentLightInstance	=	cLight->light;	\
														cLight->light->illuminate(this);					\
													}														\
												}															\
												assert(currentShadingState->numActive	==	numActive);		\
												assert(currentShadingState->numPassive	==	numPassive);	\
																											\
												currentShadingState->currentShaderInstance	=	cInstance;	\
											}



// The misc macros
#define		DEFLINKOPCODE(name,text,nargs)				case OPCODE_##name:		
#define		DEFLINKFUNC(name,text,prototype,par)		case FUNCTION_##name:

// Break the shader execution
#define		BREAK							goto execEnd;



	//	The	shading variables and junk
	TCode						**stuff[6];			// Where we keep pointers to the variables
	int							IP;					// Isn't it obvious ?
	ESlCode						opcode;				// :)
	CConditional				*lastConditional;	// The last conditional
	int							numActive;			// The number of active points being shaded
	int							numPassive;			// The number of passive points being not shaded (numPassive+numActive = numVertices)
	int							*tags;				// Execution tags
	int							*tagStart;
	int							currentVertex;		// The current vertex being executed
	CShader						*currentShader			=	cInstance->parent;
	TCode						*code;
	int							numVertices;
	float						**varying;
	CShadedLight				**lights;
	CShadedLight				*alights;
	CShadedLight				**currentLights;
	CShaderCache				*cCache;
	CGatherBundle				*lastGather;		// Pointer to the last gather bundle

	assert((currentShadingState->numActive+currentShadingState->numPassive) == currentShadingState->numVertices);

	currentShadingState->currentShaderInstance	=	cInstance;
	code										=	currentShader->codeArea;
	IP											=	currentShader->codeEntryPoint;
	tagStart									=	currentShadingState->tags;

	// Allocate the shader variables
	cCache								=	currentShader->cache;
	if (cCache == NULL)	cCache			=	newCache(currentShader);
	currentShader->cache				=	cCache->shaderNext;

	// Save this stuff for fast access
	numVertices							=	currentShadingState->numVertices;
	varying								=	currentShadingState->varying;
	lights								=	currentShadingState->lights;
	alights								=	currentShadingState->alights;
	currentLights						=	currentShadingState->currentLights;

	// Set the access arrays
	stuff[SL_IMMEDIATE_OPERAND]			=	currentShader->constantEntries;				// Immediate operands
	stuff[SL_PARAMETER_OPERAND]			=	cInstance->parameterEntries;				// Parameters
	stuff[SL_GLOBAL_OPERAND]			=	(TCode **) varying;							// Varying globals
	stuff[SL_UNIFORM_OPERAND]			=	cCache->uniforms;							// Shader uniform local variables
	stuff[SL_VARYING_OPERAND]			=	cCache->varyings;							// Shader varying local variables

	numActive							=	currentShadingState->numActive;
	numPassive							=	currentShadingState->numPassive;
	lastConditional						=	NULL;										// The last conditional block

	// Execute
execStart:
	opcode	=	(ESlCode)	code[IP].integer;	// Get the opcode

	tags	=	tagStart;						// Set the tags to the start

	if (code[IP+1].arguments.uniform) {			// If the opcode is uniform , execute once
#define		DEFOPCODE(name,text,nargs,expr_pre,expr,expr_update,expr_post,params)					\
			case OPCODE_##name:																		\
			{																						\
				expr_pre;																			\
				expr;																				\
				expr_post;																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

#define		DEFSHORTOPCODE(name,text,nargs,expr_pre,expr,expr_update,expr_post,params)				\
			case OPCODE_##name:																		\
			{																						\
				expr_pre;																			\
				expr;																				\
				expr_post;																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}


#define		DEFFUNC(name,text,prototype,expr_pre,expr,expr_update,expr_post,par)					\
			case FUNCTION_##name:																	\
			{																						\
				expr_pre;																			\
				expr;																				\
				expr_post;																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

#define		DEFSHORTFUNC(name,text,prototype,expr_pre,expr,expr_update,expr_post,par)				\
			case FUNCTION_##name:																	\
			{																						\
				expr_pre;																			\
				expr;																				\
				expr_post;																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

		switch(opcode) {

#include "scriptOpcodes.h"

#include "scriptFunctions.h"

		default:
			error(CODE_BUG,"Opcode conflict");
			goto execEnd;
		}

		// Resume executing instructions
		IP	+=	code[IP+1].arguments.numCodes;
		goto execStart;

#undef DEFOPCODE
#undef DEFSHORTOPCODE
#undef DEFFUNC
#undef DEFSHORTFUNC


	} else {
		if (numPassive != 0) {					// If we have some vertices that are passive, be more careful
#define		DEFOPCODE(name,text,nargs,expr_pre,expr,expr_update,expr_post,params)					\
			case OPCODE_##name:																		\
			{																						\
				expr_pre;																			\
				for (currentVertex=numVertices;currentVertex>0;currentVertex--,tags++) {			\
					if (*tags == 0) {																\
						expr;																		\
					}																				\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}


#define		DEFSHORTOPCODE(name,text,nargs,expr_pre,expr,expr_update,expr_post,params)				\
			case OPCODE_##name:																		\
			{																						\
				expr_pre;																			\
				for (currentVertex=currentShadingState->numRealVertices;currentVertex>0;currentVertex--,tags++) {			\
					if (*tags == 0) {																\
						expr;																		\
					}																				\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}


#define		DEFFUNC(name,text,prototype,expr_pre,expr,expr_update,expr_post,par)					\
			case FUNCTION_##name:																	\
			{																						\
				expr_pre;																			\
				for (currentVertex=numVertices;currentVertex>0;currentVertex--,tags++) {			\
					if (*tags == 0) {																\
						expr;																		\
					}																				\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

#define		DEFSHORTFUNC(name,text,prototype,expr_pre,expr,expr_update,expr_post,par)				\
			case FUNCTION_##name:																	\
			{																						\
				expr_pre;																			\
				for (currentVertex=currentShadingState->numRealVertices;currentVertex>0;currentVertex--,tags++) {			\
					if (*tags == 0) {																\
						expr;																		\
					}																				\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}


			switch(opcode) {

#include "scriptOpcodes.h"

#include "scriptFunctions.h"

			default:
				error(CODE_BUG,"Opcode conflict");
				goto execEnd;
			}

			IP	+=	code[IP+1].arguments.numCodes;
			goto execStart;
		} else {

#undef DEFOPCODE
#undef DEFSHORTOPCODE
#undef DEFFUNC
#undef DEFSHORTFUNC

#define		DEFOPCODE(name,text,nargs,expr_pre,expr,expr_update,expr_post,params)					\
			case OPCODE_##name:																		\
			{																						\
				expr_pre;																			\
				for (currentVertex=numVertices;currentVertex>0;currentVertex--) {					\
					expr;																			\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

#define		DEFSHORTOPCODE(name,text,nargs,expr_pre,expr,expr_update,expr_post,params)				\
			case OPCODE_##name:																		\
			{																						\
				expr_pre;																			\
				for (currentVertex=currentShadingState->numRealVertices;currentVertex>0;currentVertex--) {					\
					expr;																			\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}


#define		DEFFUNC(name,text,prototype,expr_pre,expr,expr_update,expr_post,par)					\
			case FUNCTION_##name:																	\
			{																						\
				expr_pre;																			\
				for (currentVertex=numVertices;currentVertex>0;currentVertex--) {					\
					expr;																			\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

#define		DEFSHORTFUNC(name,text,prototype,expr_pre,expr,expr_update,expr_post,par)				\
			case FUNCTION_##name:																	\
			{																						\
				expr_pre;																			\
				for (currentVertex=currentShadingState->numRealVertices;currentVertex>0;currentVertex--) {					\
					expr;																			\
					expr_update;																	\
				}																					\
				expr_post																			\
				IP	+=	code[IP+1].arguments.numCodes;												\
				goto execStart;																		\
			}

			switch(opcode) {

#include "scriptOpcodes.h"

#include "scriptFunctions.h"

			default:
				error(CODE_BUG,"Opcode conflict");
				goto execEnd;
			}

			IP	+=	code[IP+1].arguments.numCodes;
			goto execStart;
		}

#undef DEFOPCODE
#undef DEFSHORTOPCODE
#undef DEFFUNC
#undef DEFSHORTFUNC

	}


	goto execStart;
execEnd:

	// Restore the cache for the shader
	cCache->shaderNext		=	currentShader->cache;
	currentShader->cache	=	cCache;

	// Make sure we save the ambient contribution if there has been no illuminate/solar executed
	if (currentShader->type == SL_LIGHTSOURCE) {
		if (!(currentShader->usedParameters & PARAMETER_NONAMBIENT)) {
			float	*Cl	=	varying[VARIABLE_CL];
			float	*Ol	=	varying[VARIABLE_OL];

			// Save the ambient junk
			for (int i=0;i<numVertices;i++,Cl+=3,Ol+=3) {
				addvv(alights[i].Cl,Cl);
			}
		}
	}

// Undefine junk
#undef		MAX_SCRIPT_STRING_SIZE
#undef		savestring
#undef		allocbuffer
#undef		freebuffer
#undef		beginConditional
#undef		endConditional
#undef		operand
#undef		dirty
#undef		parameterlist
#undef		argument
#undef		argumentcount
#undef		jmp
#undef		runAmbientLights
#undef		runLights
#undef		firstLight
#undef		nextLight
#undef		allLights
#undef		currentLight
#undef		saveLight	
#undef		uniformGlobal
#undef		varyingGlobal
#undef		DEFOPCODE
#undef		DEFFUNC
#undef		DEFLINKOPCODE
#undef		DEFLINKFUNC
#undef		BREAK
}






