//================================================================
// EQUINOX-3D plugin
//
// Cg renderer
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Jan-07 22:39:50
//
// Copyright (C) 2014-2026 By Gabor Nagy. All rights reserved.
//================================================================
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <errno.h>

// cgc -profile fp30 ...

#include <ELists.h>
#include <EMalloc.h>
#include <EPlugins.h>

#include <E3D/E3D.h>
#include <E3D/Matrix.h>
#include <E3D/Material.h>
#include <E3D/Rendering.h>
#include <E3D/SL.h>

#include <E3D/UI/Panel.h>
#include <E3D/UI/StatusPanel.h>  // For E3dp_PrintMessage

#include <E3D/GL/Draw.h>

#include <E3D/SkinMesh.h>

#ifndef _E3D_PointSetDraw_h
#include <E3D/GL/PointSetDraw.h>
#endif

#include <E3D/GL/MeshDraw.h>

static EPlugin*  _Plugin = NULL;

#include "../../HW/Shadow.h"
#include "../GPURendererGUI.h"

#include "Cg.h"

#include <Cg/cgGL.h>

char*             E_PluginInfo = "GPU renderer with advanced shaders and full-screen\neffects, using the Cg shading language.";

//static const uint8_t    _PrintShaderSource = E3dSL_ALWAYS;
static const uint8_t    _PrintShaderSource = E3dSL_COMPILE_ERROR;
//static const uint8_t  _SaveShaderSourceOn = E3dSL_ALWAYS;
static const uint8_t  _SaveShaderSourceOn = E3dSL_COMPILE_ERROR;
//static const uint8_t  _SaveShaderSourceOn = E3dSL_COMPILE_ERROR|E3dSL_SYMBOL_RESOLVE_ERROR;



#define ETimerENABLE  0
#include <ETiming.h>
#if ETimerENABLE
extern ETmTimerStack  E3dp_RendererTimerStack;
#endif

static ETime  _BindMaterial_Microseconds = 0;
static ETime  _NodeHrc_Draw_Microseconds = 0;

static EBool        _ProfileInitialized = FALSE;
static const int    _FBOSharingMode = E3dFBOSharingFULL;

CGcontext	E3dCg_ShaderContext = 0;

CGprofile	E3dCg_VertexProfile = 0;
CGprofile	E3dCg_FragmentProfile = 0;


static char     _StateCompilingFragmentProgram[] = "Compiling fragment program";
static char     _StateLoadingFragmentProgram[] = "Loading fragment program";

static E3dShaderClass*		_UnlitShaderClass = NULL;
static E3dShaderClass*		_LambertShaderClass = NULL;
static E3dShaderClass*		_PhongShaderClass = NULL;


#define E3dSL_Uniform1i(pParameter, p0)              cgGLSetParameter1f(pParameter, p0)   // Cg doesn't have a way to set integer uniforms, so we have to use the float version
#define E3dSL_Uniform1f(pParameter, p0)              cgGLSetParameter1f(pParameter, p0)
#define E3dSL_Uniform2f(pParameter, p0, p1)          cgGLSetParameter2f(pParameter, p0, p1)
#define E3dSL_Uniform3f(pParameter, p0, p1, p2)      cgGLSetParameter3f(pParameter, p0, p1, p2)
#define E3dSL_Uniform4f(pParameter, p0, p1, p2, p3)  cgGLSetParameter4f(pParameter, p0, p1, p2, p3)

#define E3dSL_Uniform1fv(pParameter, pPtr)           cgGLSetParameter1fv(pParameter, pPtr)
#define E3dSL_Uniform2fv(pParameter, pPtr)           cgGLSetParameter2fv(pParameter, pPtr)
#define E3dSL_Uniform3fv(pParameter, pPtr)           cgGLSetParameter3fv(pParameter, pPtr)
#define E3dSL_Uniform4fv(pParameter, pPtr)           cgGLSetParameter4fv(pParameter, pPtr)

#define E3dSL_GetUniformfv(pShaderVariant, pParameter, pPtr)

#if E3dGL_FLOAT_BITS == 64
#define E3dSL_Uniform3(pParameter, p0, p1, p2)      E3dSL_Uniform3d(pParameter, p0, p1, p2)
#else
#define E3dSL_Uniform3(pParameter, p0, p1, p2)      E3dSL_Uniform3f(pParameter, p0, p1, p2)
#endif

#define E3dSLParameter	        CGparameter
#define E3dSLParameterValid(p)	(p != NULL)
#define E3dSLParameterNONE      NULL
#define E3dThisRenderer	        E3dCgRenderer
#define E3dThisFragmentProgram  E3dCgFragmentProgram     


static E3dFeature*  _FeatureCg  = NULL;


static EArg	 EmptyUI[]=
{
	{ NULL, 0 }
};

static EArg	 DrawZOnlyFirstUI[]=
{
	{ XeNTipStringUTF8, (EArgVal)("Draw Z-buffer only first. This might produce a better\nframe-rate if there are complex shaders in the scene.") },
	{ NULL, 0 }
};


#define ROffset(field)  (void*)(EPtrOffset(E3dThisRenderer, field))
static EResource  _Resources[]=
{
	{ "Shadows",         EResBOOLEAN,  ROffset(ShadowsOn),       .UI= EmptyUI },
	{ "Reflections",     EResBOOLEAN,  ROffset(ReflectionsOn),   .UI= EmptyUI },
	{ "Transparency",    EResBOOLEAN,  ROffset(TransparencyOn),  .UI= EmptyUI },
	{ "Z-first",         EResBOOLEAN,  ROffset(DrawZOnlyFirst),  .UI= DrawZOnlyFirstUI },

	{ "Bloom radius",    EResFLOAT32,  ROffset(Bloom.Radius),    .UI= E3dBloomUI_Radius },
	{ "Bloom strength",  EResFLOAT32,  ROffset(Bloom.Strength),  .UI= E3dBloomUI_Strength },
	{ "Bloom power",     EResFLOAT32,  ROffset(Bloom.Power),     .UI= E3dBloomUI_Power },
};
#undef ROffset


static E3dRendererClass*  _RendererClass = NULL;


#define E3dSL_SetParameterPointer(mParam, mFSize, mType, mStride, mPointer)	cgGLSetParameterPointer(mParam, mFSize, mType, mStride, mPointer)

// For example:
//
//	cgGLSetParameterPointer(LPositionParam, 3, GL_FLOAT, LPolyGroup->GLArrayVertexNodeSize_SL, LPtr);LPtr += sizeof(float) * 3;


static inline void E3dSL_EnableClientState(const E3dSLParameter PParam)
{
	cgGLEnableClientState(PParam);
}


static inline void E3dSL_DisableClientState(const E3dSLParameter PParam)
{
	cgGLDisableClientState(PParam);
}


static void _ErrorCallback(void)
{
	const CGerror  LastError = cgGetError();
	
	if(LastError)
	{
//	const char*  Listing = cgGetLastListing(E3dCg_ShaderContext);
		char*        LStr = NULL;
		char*        LPtr = NULL;
		int          LStrLen = 0;


		if(E3dSL_DebugRec.Function)
		{
			LStr = ERealloc(LStr, LStrLen + 256);LPtr = LStr + LStrLen;

			if(E3dSL_DebugRec.Line) sprintf(LPtr, "%s() %s:%d", E3dSL_DebugRec.Function, E3dSL_DebugRec.File, E3dSL_DebugRec.Line);
			else sprintf(LPtr, "%s()", E3dSL_DebugRec.Function);

			LStrLen += strlen(LPtr);LPtr = LStr + LStrLen;
		}


		if(E3dSL_DebugRec.Renderer)
		{
			LStr = ERealloc(LStr, LStrLen + 16);LPtr = LStr + LStrLen;

			sprintf(LPtr, " Frame: %lld", (long long)(E3dSL_DebugRec.Renderer->FrameCnt));

			LStrLen += strlen(LPtr);LPtr = LStr + LStrLen;
		}


		if(E3dSL_DebugRec.StateString)
		{
			const int    LLen = strlen(E3dSL_DebugRec.StateString);
			const char*  LLStr = "  State: ";const int LLLen = strlen(LLStr);

			LStr = ERealloc(LStr, LStrLen + LLLen + LLen + 1);LPtr = LStr + LStrLen;
			memcpy(LPtr, LLStr, LLLen);LPtr += LLLen;

			memcpy(LPtr, E3dSL_DebugRec.StateString, LLen + 1);LPtr += LLen;
			LStrLen = LPtr - LStr;
		}


		if(E3dSL_DebugRec.Material && E3dSL_DebugRec.Material->Name)
		{
			const int    LLen = strlen(E3dSL_DebugRec.Material->Name);
			const char*  LLStr = "  Material: ";const int LLLen = strlen(LLStr);

			LStr = ERealloc(LStr, LStrLen + LLLen + LLen + 1);LPtr = LStr + LStrLen;
			memcpy(LPtr, LLStr, LLLen);LPtr += LLLen;

			memcpy(LPtr, E3dSL_DebugRec.Material->Name, LLen + 1);LPtr += LLen;
			LStrLen = LPtr - LStr;
		}


		if(E3dSL_DebugRec.ShaderVariant && E3dSL_DebugRec.ShaderVariant->Signature)
		{
			const int    LLen = strlen(E3dSL_DebugRec.ShaderVariant->Signature);
			const char*  LLStr = "  CgID: ";const int LLLen = strlen(LLStr);

			LStr = ERealloc(LStr, LStrLen + LLLen + LLen + 1);LPtr = LStr + LStrLen;
			memcpy(LPtr, LLStr, LLLen);LPtr += LLLen;

			memcpy(LPtr, E3dSL_DebugRec.ShaderVariant->Signature, LLen + 1);LPtr += LLen;
			LStrLen = LPtr - LStr;
		}


		if(E3dSL_DebugRec.ParameterName)
		{
			const int    LLen = (int)strlen(E3dSL_DebugRec.ParameterName);
			const char*  LLStr = "  Param: ";const int  LLLen = (int)strlen(LLStr);

			LStr = ERealloc(LStr, LStrLen + LLLen + LLen + 1);LPtr = LStr + LStrLen;
			memcpy(LPtr, LLStr, LLLen);LPtr += LLLen;

			memcpy(LPtr, E3dSL_DebugRec.ParameterName, LLen + 1);LPtr += LLen;
			LStrLen = LPtr - LStr;
//assert(0);
		}


//  printf("%s :: %s\n", cgGetErrorString(LastError), Listing);fflush(stdout);

// On old nVidia drivers, this causes a lock-up with the FP30 profile
//
		App_Log(ELogWARNING, "Cg", "%s  %s", LStr, cgGetErrorString(LastError));

//assert(0);

		if(LStr) EFree(LStr);
	}
}


static int E3dSL_ProfileInit(void)
{
	if(!_ProfileInitialized)
	{
		if(cgGLIsProfileSupported(CG_PROFILE_VP40)) E3dCg_VertexProfile = CG_PROFILE_VP40;
		else if(cgGLIsProfileSupported(CG_PROFILE_VP30)) E3dCg_VertexProfile = CG_PROFILE_VP30;
		else if(cgGLIsProfileSupported(CG_PROFILE_ARBVP1)) E3dCg_VertexProfile = CG_PROFILE_ARBVP1;
		else
		{
			App_Log(ELogWARNING, "Cg", "Vertex programming extensions (GL_ARB_vertex_program or " "GL_NV_vertex_program) not supported");
			return(-1);
		}

		if(cgGLIsProfileSupported(CG_PROFILE_FP40)) E3dCg_FragmentProfile = CG_PROFILE_FP40;
		else if(cgGLIsProfileSupported(CG_PROFILE_ARBFP1)) E3dCg_FragmentProfile = CG_PROFILE_ARBFP1;
		else
		{
			App_Log(ELogWARNING, "Cg", "Fragment programming extensions (GL_ARB_fragment_program or " "GL_NV_fragment_program) not supported");
			return(-1);
		}

		cgSetErrorCallback(_ErrorCallback);
		E3dCg_ShaderContext = cgCreateContext();

		_ProfileInitialized = TRUE;
	}

	return(E_SUCCESS);
}


static void  E3dSL_CodeGeneratorInit(E3dRendererClass* PRendererClass);
static void* _BindMaterial(E3dMaterial* PMaterial, E3dRenderer* PRenderer, E3dGPUContext* PGPUContext,  E3dScene* PScene, E3dModel* PModel, E3dGeometry* PGeometry, EObject* PShadeable,  const E3dRLightInstance* PLightInstances, const EIndex PNLightInstances);
static void  _UnbindMaterial(E3dMaterial* PMaterial, void* PHWShader,  E3dRenderer* PRenderer, E3dGPUContext* PGPUContext);

static inline void E3dSL_SetTextureParameter(const E3dTextureMapper* PMapper, const E3dSLParameter PParam, const GLint PGLIndex, const int PTextureEngineIndex)
{
//printf("%s() %s:%d  TxEngine %d  PGLIndex %d\n", __FUNCTION__, __FILE__, __LINE__, PTextureEngineIndex, PGLIndex);fflush(stdout);
	cgGLSetTextureParameter(PParam, PGLIndex);
	cgGLEnableTextureParameter(PParam);
}

static void _SLVertexAttributePointer(const E3dSLParameter PSLID, const GLint PSLSize, const GLenum PType, const GLsizei PStride, const GLvoid* PPointer)
{
//printf("%s() %s:%d  %sGREEN%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);
	cgGLSetParameterPointer(PSLID,  PSLSize,  PType,  PStride, PPointer);
	cgGLEnableClientState(PSLID);
}


static int _VertexAttributePointer(const E3dStageProgram* PVertexProgram, const char* PSemantic, const char* PName, const int PIndex,  const int PASize, const uint16_t PType, const int PStride, const void* PPointer)
{
	const GLint  LGLType = EResType_ToGL(PType);

	if(PSemantic == E3dVtxAttribute_ST)
	{
		const E3dShaderDevice*  LShaderDevice = _RendererClass->ShaderDevice;
		if(LShaderDevice && LShaderDevice->BuiltIn_VertexTexCoordInParamName)
		{
			EpglClientActiveTexture(GL_TEXTURE0 + PIndex);
			EpglTexCoordPointer(PASize, LGLType, PStride, PPointer);
			EpglEnableClientState(GL_TEXTURE_COORD_ARRAY);
			return(E_SUCCESS);
		}
	}
	else if(PSemantic == E3dVtxAttribute_Normal)
	{
		const E3dShaderDevice*  LShaderDevice = _RendererClass->ShaderDevice;
		if(LShaderDevice && LShaderDevice->BuiltIn_VertexNormalInParamName)
		{
			EpglEnableClientState(GL_NORMAL_ARRAY);
			EpglNormalPointer(GL_FLOAT, PStride, PPointer);
			return(E_SUCCESS);
		}
		else
		{
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
//		_SLVertexAttributePointer(const E3dSLParameter PSLID, PASize, LGLType, PStride, PPointer);
			return(E_NOT_IMPLEMENTED);
		}
	}
	else
	{
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
//		_SLVertexAttributePointer(const E3dSLParameter PSLID, PASize, LGLType, PStride, PPointer);
		return(E_NOT_IMPLEMENTED);
	}
	return(E_NOT_SUPPORTED);
}

static int _DisableVertexAttributeArray(const E3dStageProgram* PVertexProgram, const char* PSemantic, const char* PName, const int PIndex)
{
	if(PSemantic == E3dVtxAttribute_ST)
	{
		const E3dShaderDevice*  LShaderDevice = _RendererClass->ShaderDevice;
		if(LShaderDevice && LShaderDevice->BuiltIn_VertexTexCoordInParamName)
		{
			EpglClientActiveTexture(GL_TEXTURE0 + PIndex);
			EpglDisableClientState(GL_TEXTURE_COORD_ARRAY);
			EpglActiveTexture(GL_TEXTURE0);
			return(E_SUCCESS);
		}
		return(E_NO_DATA);
	}
	else if(PSemantic == E3dVtxAttribute_Normal)
	{
		const E3dShaderDevice*  LShaderDevice = _RendererClass->ShaderDevice;
		if(LShaderDevice && LShaderDevice->BuiltIn_VertexNormalInParamName)
		{
			EpglDisableClientState(GL_NORMAL_ARRAY);
			return(E_SUCCESS);
		}
		return(E_NO_DATA);
	}
	else
	{
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
		return(E_NOT_IMPLEMENTED);
	}
}


static void _SLDisableVertexAttributeArray(const E3dSLParameter PSLID)
{
	cgGLDisableClientState(PSLID);
}

#include "../SLCommon.ci"


static void* _BindMaterial(E3dMaterial* PMaterial, E3dRenderer* PRenderer, E3dGPUContext* PGPUContext,  E3dScene* PScene, E3dModel* PModel, E3dGeometry* PGeometry, EObject* PShadeable,  const E3dRLightInstance* PLightInstances, const EIndex PNLightInstances)
{
ETimerStack_Push(&E3dp_RendererTimerStack, "Cg BindMaterial");

//const EBool  LDebug = EStr_Equal(PMaterial->Name, "Atmosphere");
//const EBool  LDebug = EStr_Equal(PMaterial->Name, "Terrain");
const EBool  LDebug = FALSE;

//const ETime  LStartMicroseconds = E_GetMicroseconds();
	EListI_s(, E3dTextureMapperPtr, LTextureMappers);
	const E3dTextureMapperPtrList*  LTextureMapperRefs;
	EBool                           LShadeableReceivesShadows = TRUE;

	if(PShadeable && EObject_IsSubClass(PShadeable, E3dPolyGroup))
	{
		const E3dPolyGroup*       LPolyGroup = (E3dPolyGroup*)PShadeable;
		const E3dVertexGeometry*  LVertexGeometry = E3dVertexGeometry_CastTo((EObject*)PGeometry);
		E3dPolyGroup_GetTextureMappers(LVertexGeometry, LPolyGroup, &LTextureMappers);
		LTextureMapperRefs = &LPolyGroup->TextureMapperRefs;
		LShadeableReceivesShadows = LPolyGroup->ReceivesShadows;
	}
	else if(PGeometry && EObject_IsSubClass(PGeometry, E3dPointSet))
	{
		LTextureMapperRefs = NULL;
		LShadeableReceivesShadows = PGeometry->ReceivesShadows;
	}
	else return(NULL);

	E3dThisRenderer*                LRenderer = (E3dThisRenderer*)PRenderer;
	E3dRendererClass*               LRendererClass = LRenderer->Class;
	E3dShader*                      LShader = PMaterial->Shader;
	E3dShaderClass*                 LShaderClass = LShader->Class;
	E3dShaderVariant*               LShaderVariant = NULL;
	E3dShaderClassRendererBinding*  LShaderClassBinding = (E3dShaderClassRendererBinding*)E3dShader_ClassGetRendererBinding(LShader->Class, LRendererClass);if(LShaderClassBinding)
	{
#ifndef EGLES
		EpglDisable(GL_LIGHTING);
#endif

		E3dMaterial_SLUpdateNormalMaps(PScene, PRenderer, PMaterial,  &LTextureMappers, LTextureMapperRefs);

		_SL_BindMaterial0();

//printf("%s() %s:%d LShaderVariant [\n%s\n]\n", __FUNCTION__, __FILE__, __LINE__, LShaderVariant->VertexProgram->Source);fflush(stdout);
//printf("%s() %s:%d LShaderVariant [\n%s\n]\n", __FUNCTION__, __FILE__, __LINE__, LShaderVariant->FragmentProgram->Source);fflush(stdout);

// For _ErrorCallback
//
		E3dSL_DebugRec.Material = PMaterial;
		E3dSL_DebugRec.Shader = LShader;
		E3dSL_DebugRec.ShaderVariant = LShaderVariant;
		E3dSL_DebugRec.ParameterName = NULL;

if(LDebug) { printf("%s() %s:%d  [%s%s%s]  LShaderVariant %p  LShaderVariant->VertexProgram %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal,  LShaderVariant, LShaderVariant->VertexProgram);fflush(stdout); }
		if(LShaderVariant->VertexProgram == NULL) { LShaderVariant->CreationResult = E_NO_PROGRAM;LShaderVariant = NULL;goto End; }
if(LDebug) { printf("%s() %s:%d  [%s%s%s]  LShaderVariant %p  LShaderVariant->VertexProgram %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal,  LShaderVariant, LShaderVariant->VertexProgram);fflush(stdout); }

		E3dThisVertexProgram*  LThisVertexProgram = (E3dThisVertexProgram*)LShaderVariant->VertexProgram;
		E3dCgFragmentProgram*  LFragmentProgram = (E3dCgFragmentProgram*)LShaderVariant->FragmentProgram;
		E3dThisShaderVariant*  LThisShaderVariant = (E3dThisShaderVariant*)LShaderVariant;

if(LDebug) { printf("%s() %s:%d  [%s%s%s]  %p %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal,  LShaderVariant, LShaderVariant->VertexProgram, LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout); }
		if(LShaderVariant->CreationResult == E_NOT_SET)  // Only try to create this Cg shader, if we haven't tried yet (if it failed, there's no point spitting out the same errors every frame)
		{
if(LDebug) { printf("%s() %s:%d  [%s%s%s]  %p %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal, LShaderVariant, LShaderVariant->VertexProgram, LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout); }
			EBool  LSaveShaderSource = FALSE;

// Compile the vertex program
//
			if(LThisVertexProgram->Program == NULL)
			{
if(LDebug) { printf("%s() %s:%d  [%s%s%s]  %p %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal, LShaderVariant, LShaderVariant->VertexProgram, LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout); }
ETimerStack_Push(&E3dp_RendererTimerStack, "VertexProgram");
				E3dSL_DebugRec.StateString = "Compiling vertex-program";E3dSL_DebugRec.Material = PMaterial;E3dSL_DebugRec.ShaderVariant = NULL;E3dSL_DebugRec.ParameterName = NULL;
				LThisVertexProgram->Program = cgCreateProgram(E3dCg_ShaderContext, CG_SOURCE, LThisVertexProgram->Source, E3dCg_VertexProfile, "main", NULL);
//printf("%s() %s:%d [%d:%d]\n", __FUNCTION__, __FILE__, __LINE__, CG_SOURCE, E3dCg_VertexProfile);fflush(stdout);
				E3dSL_ClearDebugRec();

				if(LThisVertexProgram->Program)
				{
if(LDebug) { printf("%s() %s:%d  [%s%s%s]  %p %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal, LShaderVariant, LShaderVariant->VertexProgram, LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout); }
					cgGLLoadProgram(LThisVertexProgram->Program);

					const E3dPolyGroup*       LPolyGroup = E_DynamicCast(PShadeable, E3dPolyGroup);
					const E3dVertexGeometry*  LVertexGeometry = E3dVertexGeometry_CastTo((EObject*)PGeometry);
					const EIndex              LNTextureCoordSetsTotal = E3dPolyGroup_GetTextureMappers(LVertexGeometry, LPolyGroup,  NULL);
					EBool                     LMappersHaveTxTg[LNTextureCoordSetsTotal + 1];EClr_s(LMappersHaveTxTg);  // Use +1, in case LNTextureCoordSets is 0

					if(LPolyGroup) _E3dPolyGroup_GetHaveTxTgFlags(PModel, LVertexGeometry, LPolyGroup, LMappersHaveTxTg);
					_E3dSL_VertexProgramResolveSymbols(PRenderer, LShaderClassBinding,  PGeometry, PShadeable,  LThisShaderVariant, PMaterial,  &LTextureMappers, LMappersHaveTxTg,  NULL);
				}
				else
				{
					App_Log(ELogWARNING, PRenderer->Class->Name, "Unable to compile vertex program for '%s'", LShaderVariant->Signature);
					LSaveShaderSource = TRUE;
					LShaderVariant->CreationResult = E_COMPILE_ERROR;
				}

/*
printf("%s() %s:%d  %s%s%s  %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LShaderVariant->Signature, LSaveShaderSource);fflush(stdout);
				if((LSaveShaderSource || _SaveShaderSourceOn == E3dSL_ALWAYS) && LShaderVariant->Signature)
				{
					char*  LFileName = EMalloc(strlen(PMaterial->Name) + strlen(LShaderVariant->Signature) + 1 + 16);

					sprintf(LFileName, "%s%s-v.cg", PMaterial->Name, LShaderVariant->Signature);

					if(LThisVertexProgram->Source)
					{
						FILE*  LFile = fopen(LFileName, "w");if(LFile)
						{
							fwrite(LThisVertexProgram->Source, strlen(LThisVertexProgram->Source), 1, LFile);
							fclose(LFile);
						}
					}
				}
*/
ETimerStack_Pop(&E3dp_RendererTimerStack);
			}


if(_PrintShaderSource == E3dSL_ALWAYS)
{
	printf("%s() %s:%d  |%s| %sVertexProgram%s\n", __FUNCTION__, __FILE__, __LINE__, PMaterial->Name, ECodeGreen, ECodeNormal);fflush(stdout);
	EStr_PrintWithLineNumbers(LShaderVariant->VertexProgram->Source, ECodeNormal);
	printf("%s() %s:%d  |%s| %sFragmentProgram%s [%s]\n", __FUNCTION__, __FILE__, __LINE__, PMaterial->Name, ECodeGreen, ECodeNormal, LShaderVariant->Signature);fflush(stdout);
	EStr_PrintWithLineNumbers(LShaderVariant->FragmentProgram->Source, ECodeNormal);
}


// Compile the fragment program and resolve symbols in it
//
			if(LFragmentProgram->Source && LFragmentProgram->Program == NULL)
			{
if(LDebug) { printf("%s() %s:%d  [%s%s%s]  %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal,  LShaderVariant, LFragmentProgram);fflush(stdout); }

ETimerStack_Push(&E3dp_RendererTimerStack, "Fragment program");
				E3dSL_Location();E3dSL_DebugRec.StateString = _StateCompilingFragmentProgram;E3dSL_DebugRec.Material = PMaterial;E3dSL_DebugRec.ShaderVariant = LShaderVariant;
				LFragmentProgram->Program = cgCreateProgram(E3dCg_ShaderContext, CG_SOURCE, LFragmentProgram->Source, E3dCg_FragmentProfile, "main", NULL);
				E3dSL_ClearDebugRec();

				if(!LFragmentProgram->Program)
				{
					printf("%s() %s:%d  |%s| %sFragmentProgram compile error%s [%s]\n", __FUNCTION__, __FILE__, __LINE__, PMaterial->Name, ECodeYellow, ECodeNormal, LShaderVariant->Signature);fflush(stdout);
					if(_PrintShaderSource & E3dSL_COMPILE_ERROR) EStr_PrintWithLineNumbers(LShaderVariant->FragmentProgram->Source, ECodeYellow);
					LSaveShaderSource = TRUE;
				}

//printf("%s() %s:%d [%d:%d]\n", __FUNCTION__, __FILE__, __LINE__, CG_SOURCE, E3dCg_FragmentProfile);fflush(stdout);

				if(LFragmentProgram->Program)
				{
					E3dSL_DebugRec.StateString = _StateLoadingFragmentProgram;
					cgGLLoadProgram(LFragmentProgram->Program);

//E3dSLTEXTURE
E3dSL_Location();
					E3dSLM_ShaderResolveUniforms(PRenderer, LShaderClassBinding, PMaterial, LShaderVariant,  LNumOfTextures, LTextureMapperRefs,  LNLightsForPass, LShadowMaps);
E3dSL_ClearDebugRec();

/*
					if(LShaderVariant->TextureBlenderLists)
					{
						for(LI = 0;LI < LShaderVariant->Textures_Count;LI++)
						{
							EFree(LShaderVariant->TextureBlenderLists[LI]);
						}
						EFree(LShaderVariant->TextureBlenderLists);LShaderVariant->TextureBlenderLists = NULL;
					}
*/
				}
				else
				{
					LSaveShaderSource = TRUE;

					App_Log(ELogMESSAGE, "Cg", "%s() Error calling '%s'. Saving as '%s.cg' for debugging", __FUNCTION__, PMaterial->Name, LShaderVariant->Signature);fflush(stdout);
					App_Log(ELogWARNING, "Cg", "Shader-class %s: Unable to load fragment program: %s()", LShaderClass->Name, LShaderVariant->Signature);
					LShaderVariant->CreationResult = E_NO_PROGRAM;
				}
ETimerStack_Pop(&E3dp_RendererTimerStack);
			}

printf("%s() %s:%d  %s%s%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LShaderVariant->Signature);fflush(stdout);
			if((LSaveShaderSource || _SaveShaderSourceOn == E3dSL_ALWAYS) && LShaderVariant->Signature)
			{
				char*  LFileName = EMalloc(strlen(PMaterial->Name) + strlen(LShaderVariant->Signature) + 1 + 16);

printf("%s() %s:%d  %s%s%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal, LShaderVariant->Signature);fflush(stdout);
				if(LThisVertexProgram->Source)
				{
					sprintf(LFileName, "%s_%s-v.cg", PMaterial->Name, LShaderVariant->Signature);
//printf("%s() %s:%d  %s\n", __FUNCTION__, __FILE__, __LINE__, LFileName);fflush(stdout);
					FILE*  LFile = fopen(LFileName, "w");if(LFile)
					{
						fwrite(LThisVertexProgram->Source, strlen(LThisVertexProgram->Source), 1, LFile);
						fclose(LFile);
					}
					else
					{
printf("%s() %s:%d  %sfopen() failed%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
E_errnoPrintString(errno);
					}
				}

				if(LShaderVariant->FragmentProgram->Source)
				{
					sprintf(LFileName, "%s_%s-f.cg", PMaterial->Name, LShaderVariant->Signature);
//printf("%s() %s:%d  %s\n", __FUNCTION__, __FILE__, __LINE__, LFileName);fflush(stdout);
					FILE*  LFile = fopen(LFileName, "w");if(LFile)
					{
						fwrite(LShaderVariant->FragmentProgram->Source, strlen(LShaderVariant->FragmentProgram->Source), 1, LFile);
						fclose(LFile);
					}
					else
					{
printf("%s() %s:%d  %sfopen() failed%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
E_errnoPrintString(errno);
					}
				}

				EFree(LFileName);
			}

			if(LShaderVariant->CreationResult != E_SUCCESS && LShaderVariant->CreationResult != E_NOT_SET && LShaderVariant->CreationResult != E_UNDEFINED_SYMBOL) { LShaderVariant = NULL;goto End; }

			LShaderVariant->CreationResult = E_SUCCESS;
		}

		if(LShaderVariant->CreationResult != E_SUCCESS) { LShaderVariant = NULL;goto End; }

E3dSL_ClearDebugRec();
ETimerStack_Push(&E3dp_RendererTimerStack, "Bind programs");
E3dSL_Location();
		if(LThisVertexProgram->Program)
		{
			cgGLBindProgram(LThisVertexProgram->Program);
			cgGLEnableProfile(E3dCg_VertexProfile);
		}
		if(LFragmentProgram->Program)
		{
			cgGLBindProgram(LFragmentProgram->Program);
			cgGLEnableProfile(E3dCg_FragmentProfile);
		}
ETimerStack_Pop(&E3dp_RendererTimerStack);


E3dSL_ClearDebugRec();
ETimerStack_Push(&E3dp_RendererTimerStack, "VertexProgram inputs");
// Set vertex program inputs
//
//  cgGLSetStateMatrixParameter(LThisVertexProgram->ModelViewMatrixParam, CG_GL_MODELVIEW_MATRIX, CG_GL_MATRIX_IDENTITY);
E3dSL_Location();
		cgGLSetStateMatrixParameter(LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
E3dSL_ClearDebugRec();


ETimerStack_Push(&E3dp_RendererTimerStack, "VertexProgram uniforms");
// Set vertex-program uniform parameters
//
		if(LShaderVariant->VertexProgram)
		{
// VertexProgram uniforms, if any exist
//
//printf("%s() %s:%d  [%s%s%s]  %p %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal, LShaderVariant, LShaderVariant->VertexProgram, LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout);
			if(E3dSLParameterValid(LThisVertexProgram->Param_VtU_EyeLocalPosition)) E3dSL_Uniform3f(LThisVertexProgram->Param_VtU_EyeLocalPosition,  LEyePos.X, LEyePos.Y, LEyePos.Z);
			if(LThisVertexProgram->ResourceParameters)
			{
				E3dSL_ShaderSetUniforms(PScene,  LLightInstancesForRenderPass, LNLightsForPass,  LRenderer, PMaterial, LThisShaderVariant, LShaderClassBinding, E3dShaderStageVERTEX, LThisVertexProgram->ResourceParameters,  LWorldToLocalMatrix,  LWorldToLocalUnitVectorMatrix,  &LCameraPosition, &LEyePos,  LRenderer->AmbientMult, &E3dSL_DebugRec);
			}
		}
/*
		else
		{
printf("%s() '%s' No LThisShaderVariant->VertexProgram->ResourceParameters\n", __FUNCTION__, PMaterial->Name);fflush(stdout);
		}
*/
ETimerStack_Pop(&E3dp_RendererTimerStack);


// Set FragmentProgram uniform parameters, if any exist
//
		if(LShaderVariant->GeometryProgram)
		{
//		if(((E3dCgGeometryProgram*)LShaderVariant->GeometryProgram)->ResourceParameters)
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
		}


if(LDebug) { printf("%s() %s:%d  [%s%s%s]  %p %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal, LShaderVariant, LShaderVariant->VertexProgram, LThisVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout); }

// Set FragmentProgram uniform parameters
//
		if(LFragmentProgram->Program)
		{
ETimerStack_Push(&E3dp_RendererTimerStack, "FragmentProgram uniforms");

	ETimerStack_Push(&E3dp_RendererTimerStack, "E3dSL_ShaderSetUniforms");
			E3dSL_ShaderSetUniforms(PScene,  LLightInstancesForRenderPass, LNLightsForPass,  LRenderer, PMaterial, LThisShaderVariant, LShaderClassBinding, E3dShaderStageFRAGMENT, LThisShaderVariant->Param_FgU_Custom,  LWorldToLocalMatrix, LWorldToLocalUnitVectorMatrix,  &LCameraPosition, &LEyePos,  LRenderer->AmbientMult, &E3dSL_DebugRec);
	ETimerStack_Pop(&E3dp_RendererTimerStack);

	ETimerStack_Push(&E3dp_RendererTimerStack, "cgGLEnableTextureParameter calls");
			{
				int  LI = 0;E3dSLLight*  LSHLight = LThisShaderVariant->Lights;for(;LI < LNLightsForPass;LI++, LSHLight++)
				{
					if(E3dSLParameterValid(LSHLight->Param_ShadowMap)) { E3dSL_DebugRec.ParameterName = "ShadowMap";cgGLEnableTextureParameter(LSHLight->Param_ShadowMap); }
				}
			}
	ETimerStack_Pop(&E3dp_RendererTimerStack);

	ETimerStack_Push(&E3dp_RendererTimerStack, "E3dSL_SetTextureParams");
			E3dSL_SetTextureParams(PScene, LRenderer, PModel, PMaterial, LThisShaderVariant,  &LTextureMappers, LTextureMapperRefs,  LLightInstancesForRenderPass, LNLightsForPass);
	ETimerStack_Pop(&E3dp_RendererTimerStack);


ETimerStack_Pop(&E3dp_RendererTimerStack);
		}
	}

//E_TimeStamp(PMaterial->Name);

//if(ETimerENABLE) E_NamedTime(_BindMaterial);
ETimerStack_Pop(&E3dp_RendererTimerStack);

End:
	EList_ObjectsFree_s(LTextureMappers);

	return(LShaderVariant);
}


static void _UnbindMaterial(E3dMaterial* PMaterial, void* PHWShader,  E3dRenderer* PRenderer, E3dGPUContext* PGPUContext)
{
	E3dThisRenderer*       LThisRenderer = (E3dThisRenderer*)PRenderer;
	E3dThisShaderVariant*  LThisShaderVariant = (E3dThisShaderVariant*)PHWShader;

	E3dSL_Location();

	if(LThisShaderVariant)
	{
		{
			EIndex  LI = 0;const int  LN = LThisRenderer->MaxNLightsPerPass;E3dSLLight*  LSHLight = LThisShaderVariant->Lights;for(;LI < LN;LI++, LSHLight++)
			{
				if(E3dSLParameterValid(LSHLight->Param_ShadowMap)) cgGLDisableTextureParameter(LSHLight->Param_ShadowMap);
			}
		}

		{
			EIndex  LI = 0;const EIndex  LN = PMaterial->Textures.Count;for(;LI < LN;LI++)
			{
				if(E3dSLParameterValid(LThisShaderVariant->Param_FgU_Textures[LI])) cgGLDisableTextureParameter(LThisShaderVariant->Param_FgU_Textures[LI]);
				if(E3dSLParameterValid(LThisShaderVariant->Param_FgU_NormalMaps[LI])) cgGLDisableTextureParameter(LThisShaderVariant->Param_FgU_NormalMaps[LI]);
			}
		}
	}

	cgGLDisableProfile(E3dCg_FragmentProfile);
	cgGLDisableProfile(E3dCg_VertexProfile);

/*
	{
//printf("%s() %s:%d  [%s] TextureEnginesUsed %d\n", __FUNCTION__, __FILE__, __LINE__, PMaterial->Name, LShaderVariant->TextureEnginesUsed);fflush(stdout);
		int  LI = LShaderVariant->TextureEnginesUsed - 1;for(;LI > 0;LI--)
		{
			EpglActiveTexture(GL_TEXTURE0 + LI);
			E3dGL_TexturesDisable();
		}

		EpglActiveTexture(GL_TEXTURE0);
	}
*/

	E3dSL_ClearDebugRec();
}


static void _InfoParse(E3dThisRenderer* PRenderer,  E3dModel* PModel, E3dGeometry* PGeometry, EObject* PShadeable,  va_list PVArgs, const EBool PGenerateShader)
{
	E3dMaterial*        LMaterial;
//E3dTextureMapper**  LTextureMappers;

	if(EObject_IsSubClass(PShadeable, E3dPolyGroup))
	{
		E3dPolyGroup*  LPolyGroup = (E3dPolyGroup*)PShadeable;
		LMaterial = LPolyGroup->Material ? LPolyGroup->Material : E3dObject_GetDrawingMaterial(NULL, (E3dObject*)PGeometry, TRUE);

//	LTextureMappers = LPolyGroup->TextureMappers;
	}
	else return;

	E3dThisShaderVariant*  LThisShaderVariant = NULL;
	short                  LArgType;

	EResourceList*         LVtxProgramResources = NULL;
	EResourceList*         LFrgProgramResources = NULL;

	while((LArgType = (short)va_arg(PVArgs, int)) != 0)  // Get the argument type, is it a terminating 0 ?
	{
		switch(LArgType)
		{
			case E3dSHADER_ID:
				{
					char**  LIDPtr = (char**)va_arg(PVArgs, void*);

					if(PGenerateShader)
					{
						if(LIDPtr) *LIDPtr = NULL;
						if(LMaterial->Shader && LMaterial->Shader->Class)
						{
							E3dShaderClassRendererBinding*	LShaderClassBinding = (E3dShaderClassRendererBinding*)E3dShader_ClassGetRendererBinding(LMaterial->Shader->Class, PRenderer->Class);

							if(LShaderClassBinding && LThisShaderVariant == NULL)
							{
//printf("%s() %s:%d E3dSHADER_ID\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
								LThisShaderVariant = (E3dThisShaderVariant*)E3dShader_GenerateCode(PModel, PGeometry, PShadeable, (E3dRenderer*)PRenderer, LShaderClassBinding,  E3dShaderPassBASE|E3dShaderPassLIGHTADD,  NULL, 1, NULL,  LFrgProgramResources, LVtxProgramResources,  _PrintShaderSource == E3dSL_ALWAYS || (_SaveShaderSourceOn == E3dSL_ALWAYS));
							}

							if(LThisShaderVariant)
							{
								if(LIDPtr) *LIDPtr = LThisShaderVariant->Signature;
							}
						}
					}
				}
			break;

			case E3dVERTEX_PROGRAM_RESOURCES:
				{
					LVtxProgramResources = (EResourceList*)va_arg(PVArgs, void*);

					if(PGenerateShader)
					{
						if(LMaterial->Shader && LMaterial->Shader->Class)
						{
							E3dShaderClassRendererBinding*  LShaderClassBinding = (E3dShaderClassRendererBinding*)E3dShader_ClassGetRendererBinding(LMaterial->Shader->Class, PRenderer->Class);

							if(LShaderClassBinding && LThisShaderVariant == NULL)
							{
//printf("%s() E3dVERTEX_PROGRAM_RESOURCES %s:%d \n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
								LThisShaderVariant = (E3dThisShaderVariant*)E3dShader_GenerateCode(PModel, PGeometry, PShadeable,  (E3dRenderer*)PRenderer, LShaderClassBinding,  E3dShaderPassBASE|E3dShaderPassLIGHTADD,  NULL, 1, NULL,  LFrgProgramResources, LVtxProgramResources,  _PrintShaderSource == E3dSL_ALWAYS || _SaveShaderSourceOn == E3dSL_ALWAYS);
							}
						}
					}
				}
			break;

			case E3dVERTEX_PROGRAM_SOURCE:
				{
					int     LIndex = (int)va_arg(PVArgs, int);
					char**  LSourcePtr = (char**)va_arg(PVArgs, void*);
					char**  LSignaturePtr = (char**)va_arg(PVArgs, void*);

					if(LSourcePtr) *LSourcePtr = NULL;
					if(LSignaturePtr) *LSignaturePtr = NULL;

					if(PGenerateShader)
					{
						if(LMaterial->Shader && LMaterial->Shader->Class)
						{
							E3dShaderClassRendererBinding*	LShaderClassBinding = (E3dShaderClassRendererBinding*)E3dShader_ClassGetRendererBinding(LMaterial->Shader->Class, PRenderer->Class);

							if(LShaderClassBinding)
							{
								if(LThisShaderVariant == NULL)
								{
//printf("%s() E3dVERTEX_PROGRAM_SOURCE %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
									LThisShaderVariant = (E3dThisShaderVariant*)E3dShader_GenerateCode(PModel, PGeometry, PShadeable, (E3dRenderer*)PRenderer, LShaderClassBinding,  E3dShaderPassBASE|E3dShaderPassLIGHTADD,  NULL, 1, NULL,  LFrgProgramResources, LVtxProgramResources,  _PrintShaderSource == E3dSL_ALWAYS || _SaveShaderSourceOn == E3dSL_ALWAYS);

// Only LIndex == 0 is valid for generated vertex programs
//
									if(LIndex == 0 && LThisShaderVariant && LThisShaderVariant->VertexProgram)
									{
										if(LSourcePtr) *LSourcePtr = LThisShaderVariant->VertexProgram->Source;
									}
								}
							}
						}
					}
				}
			break;

			case E3dFRAGMENT_PROGRAM_RESOURCES:
				{
					LFrgProgramResources = (EResourceList*)va_arg(PVArgs, void*);

					if(PGenerateShader)
					{
						if(LMaterial->Shader && LMaterial->Shader->Class)
						{
							E3dShaderClassRendererBinding*	LShaderClassBinding = (E3dShaderClassRendererBinding*)E3dShader_ClassGetRendererBinding(LMaterial->Shader->Class, PRenderer->Class);

							if(LShaderClassBinding && LThisShaderVariant == NULL)
							{
//printf("%s() E3dFRAGMENT_PROGRAM_RESOURCES %s:%d\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
								LThisShaderVariant = (E3dThisShaderVariant*)E3dShader_GenerateCode(PModel, PGeometry, PShadeable, (E3dRenderer*)PRenderer, LShaderClassBinding,  E3dShaderPassBASE|E3dShaderPassLIGHTADD,  NULL, 1, NULL,  LFrgProgramResources, LVtxProgramResources,  _PrintShaderSource == E3dSL_ALWAYS || _SaveShaderSourceOn == E3dSL_ALWAYS);
							}
						}
					}
				}
			break;

			case E3dFRAGMENT_PROGRAM_SOURCE:
				{
					char**  LSourcePtr = (char**)va_arg(PVArgs, void*);

					if(LSourcePtr) *LSourcePtr = NULL;

					if(PGenerateShader)
					{
						if(LMaterial->Shader && LMaterial->Shader->Class)
						{
							E3dShaderClassRendererBinding*	LShaderClassBinding = (E3dShaderClassRendererBinding*)E3dShader_ClassGetRendererBinding(LMaterial->Shader->Class, PRenderer->Class);

							if(LShaderClassBinding && LThisShaderVariant == NULL)
							{
//printf("%s() %s:%d E3dFRAGMENT_PROGRAM_SOURCE\n", __FUNCTION__, __FILE__, __LINE__);fflush(stdout);
								LThisShaderVariant = (E3dThisShaderVariant*)E3dShader_GenerateCode(PModel, PGeometry, PShadeable, (E3dRenderer*)PRenderer, LShaderClassBinding,  E3dShaderPassBASE|E3dShaderPassLIGHTADD,  NULL, 1, NULL,  LFrgProgramResources, LVtxProgramResources,  _PrintShaderSource == E3dSL_ALWAYS || _SaveShaderSourceOn == E3dSL_ALWAYS);
							}
						}

						if(LThisShaderVariant)
						{
							E3dCgFragmentProgram*  LFragmentProgram = (E3dCgFragmentProgram*)LThisShaderVariant->FragmentProgram;
							if(LSourcePtr)
							{
								if(LFragmentProgram && LFragmentProgram->Source) *LSourcePtr = LFragmentProgram->Source;
								else *LSourcePtr = NULL;
							}
						}
					}
				}
			break;
		}
	}
}


//================================================
// Info member function of the RendererClass
//================================================
static int _Info(E3dRenderer* PRenderer,  E3dModel* PModel, E3dGeometry* PGeometry, EObject* PShadeable,  const int PAction, ...)
{
	switch(PAction)
	{
		case E3dGET:
			{
				E3dThisRenderer*  LRenderer = (E3dThisRenderer*)PRenderer;

				va_list  LVArgs;

// Parse arglist to see if we need FrgProgramResources and/or VtxProgramResources
//
				va_start(LVArgs, PAction);        // PAction is the last argument before the varargs
					_InfoParse(LRenderer,  PModel, PGeometry, PShadeable,  LVArgs, FALSE);
				va_end(LVArgs);

				va_start(LVArgs, PAction);        // PAction is the last argument before the varargs
					_InfoParse(LRenderer,  PModel, PGeometry, PShadeable,  LVArgs, TRUE);
				va_end(LVArgs);
			}
		break;
	}

	return(E_SUCCESS);
}


static void _GeometryProgramFree(E3dCgGeometryProgram* LProgram)
{
	if(LProgram->RefCnt > 0) LProgram->RefCnt -= 1;
	if(LProgram->RefCnt > 0) return;

	if(LProgram->Program) cgDestroyProgram(LProgram->Program);
	if(LProgram->Source && !LProgram->SourceStatic) EFree(LProgram->Source);

	EFree(LProgram);
}


static void _FragmentProgramFree(E3dCgFragmentProgram* LProgram)
{
	if(LProgram->RefCnt > 0) LProgram->RefCnt -= 1;
	if(LProgram->RefCnt > 0) return;

	if(LProgram->Program) cgDestroyProgram(LProgram->Program);
	if(LProgram->Source && !LProgram->SourceStatic) EFree(LProgram->Source);

	EFree(LProgram);
}


//================================================
// Free Cg bindings of a ShaderClass
//================================================
static void _ShaderClassFree(E3dShaderClass* PShaderClass, E3dShaderClassRendererBinding* PShaderClassRendererBinding)
{
	E3dShaderClassRendererBinding*  LShaderClassRendererBinding = (E3dShaderClassRendererBinding*)PShaderClassRendererBinding;

	if(LShaderClassRendererBinding->ShaderVariants.Items)
	{
//printf("%s() %s:%d  %s%s%s ShaderVariants %lld\n", __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, PShaderClass->Name, ECodeNormal,  (long long)LShaderClassRendererBinding->ShaderVariants.Count);fflush(stdout);
		E3dRendererClass*       LRendererClass = (E3dRendererClass*)LShaderClassRendererBinding->RendererClass;
		const E3dShaderDevice*  LShaderDevice = LRendererClass->ShaderDevice;

		EIndex  LSVI = 0;const EIndex  LSVN = LShaderClassRendererBinding->ShaderVariants.Count;for(;LSVI < LSVN;LSVI++)
		{
			E3dThisShaderVariant*  LShaderVariant = (E3dThisShaderVariant*)LShaderClassRendererBinding->ShaderVariants.Items[LSVI];

			if(LShaderVariant->GeometryProgram) _GeometryProgramFree((E3dCgGeometryProgram*)LShaderVariant->GeometryProgram);
			if(LShaderVariant->FragmentProgram) _FragmentProgramFree((E3dCgFragmentProgram*)LShaderVariant->FragmentProgram);

			if(LShaderVariant->TextureBlendingParameters)
			{
				int  LTxI = 0;const int  LTxN = LShaderVariant->NumOfTextures;for(;LTxI < LTxN;LTxI++)
				{
					EFreeChk(LShaderVariant->TextureBlendingParameters[LTxI]);
				}

				EFree(LShaderVariant->TextureBlendingParameters);
			}

			if(LShaderVariant->VertexProgram) LShaderDevice->VertexProgramFree(LShaderVariant->VertexProgram);

			E3dShaderVariant_CoreFree((E3dShaderVariant*)LShaderVariant);
		}

		EList_FreeA_s(LShaderClassRendererBinding->ShaderVariants);
	}
}



//
// Shader code generator setup
//

static void _ShaderVariantInit(E3dShaderVariant* PShaderVariant)
{
	E3dThisShaderVariant*  LShaderVariant = (E3dThisShaderVariant*)PShaderVariant;

	LShaderVariant->Param_FgU_LocalToWorldUnitVectorMatrix = E3dSLParameterNONE;
	LShaderVariant->Param_FgU_EyeLocalPosition = E3dSLParameterNONE;
	LShaderVariant->Param_FgU_EyeWorldPosition = E3dSLParameterNONE;
	LShaderVariant->Param_FgU_Alpha = E3dSLParameterNONE;
	LShaderVariant->Param_FgU_Fresnel0_RGB = E3dSLParameterNONE;

	{
		EIndex  LI = 0;for(;LI < E3dSLMAX_NUM_OF_PARAMETERS;LI++)
		{
			LShaderVariant->Param_FgU_Custom[LI] = E3dSLParameterNONE;
		}
	}

	LShaderVariant->Param_FgU_NumOfLights = E3dSLParameterNONE;
	{
		EIndex  LI = 0;E3dSLLight*  LSHLight = LShaderVariant->Lights;for(;LI < E3dSLMAX_NUM_OF_LIGHTS;LI++, LSHLight++)
		{
			LSHLight->Param_Position = E3dSLParameterNONE;
			LSHLight->Param_Color = E3dSLParameterNONE;
			LSHLight->Param_DiffuseSpecularFactors = E3dSLParameterNONE;
			LSHLight->Param_ShadowMap = E3dSLParameterNONE;
		}
	}

	{
		EIndex  LI = 0;for(;LI < E3dSLMAX_NUM_OF_2DTEXTURES;LI++)
		{
			LShaderVariant->Param_FgU_Textures[LI] = E3dSLParameterNONE;
			LShaderVariant->Param_FgU_TextureBRDFSamplesSqrt[LI] = E3dSLParameterNONE;
			LShaderVariant->Param_FgU_TextureEnvLightFactor[LI] = E3dSLParameterNONE;
			LShaderVariant->Param_FgU_Eta[LI] = E3dSLParameterNONE;
			LShaderVariant->Param_FgU_NormalMaps[LI] = E3dSLParameterNONE;
		}
	}
}

// Resource-translator for sampler2D
//
static const char  _sampler2D[] = "sampler2D";
static const char  _tex2D[] = "tex2D";

static int _RT_sampler2D(EResContext* PContext, const void* PDStructAddress, const void* PSPtr, void* PDPtr, int PAction)
{
	switch(PAction)
	{
		case EResGET_TYPE_NAME:
			*((const char**)PDPtr) = _sampler2D;
		return(E_SUCCESS);

		case EResGET_FUNC_NAME:
			*((const char**)PDPtr) = _tex2D;
		return(E_SUCCESS);
	}
	return(E_NOTHING_TO_DO);
}


// Resource-translator for sampler3D
//
static const char  _sampler3D[] = "sampler3D";

static int _RT_sampler3D(EResContext* PContext, const void* PDStructAddress, const void* PSPtr, void* PDPtr, const int PAction)
{
	switch(PAction)
	{
		case EResGET_TYPE_NAME:
			*((const char**)PDPtr) = _sampler3D;
		return(E_SUCCESS);

		case EResSET_VALUE:
			{
				cgGLSetTextureParameter((CGparameter)PSPtr, *((GLuint*)PDPtr));
				cgGLEnableTextureParameter((CGparameter)PSPtr);
			}
		return(E_SUCCESS);
	}
	return(E_NOTHING_TO_DO);
}


// Resource-translator for samplerCUBE
//
static const char  _samplerCUBE[] = "samplerCUBE";
static const char  _texCUBE[] = "texCUBE";

static int _RT_samplerCUBE(EResContext* PContext, const void* PDStructAddress, const void* PSPtr, void* PDPtr, const int PAction)
{
	switch(PAction)
	{
		case EResGET_TYPE_NAME:
			*((const char**)PDPtr) = _samplerCUBE;
		return(E_SUCCESS);

		case EResGET_FUNC_NAME:
			*((const char**)PDPtr) = _texCUBE;
		return(E_SUCCESS);
	}
	return(E_NOTHING_TO_DO);
}


// Resource-translator for float3x3
//
static const char  _float3x3[] = "float3x3";
static const char  _float3x3_Default[] = "\n1 0 0\n0 1 0\n0 0 1";

static int _RT_float3x3(EResContext* PContext, const void* PDStructAddress, const void* PSPtr, void* PDPtr, const int PAction)
{
	switch(PAction)
	{
		case EResGET_TYPE_NAME:
			*((const char**)PDPtr) = _float3x3;
		return(E_SUCCESS);

		case EResGET_DEFAULT_VALUE_STRING:
			*((const char**)PDPtr) = _float3x3_Default;
		return(E_SUCCESS);
	}
	return(E_NOTHING_TO_DO);
}


// Resource-translator for float3x3
//
static const char  _float3x4[] = "float3x4";
static const char  _float3x4_Default[] = "\n1 0 0\n0 1 0\n0 0 1\n0 0 0";

static int _RT_float3x4(EResContext* PContext, const void* PDStructAddress, const void* PSPtr, void* PDPtr, const int PAction)
{
	switch(PAction)
	{
		case EResGET_TYPE_NAME:
			*((const char**)PDPtr) = _float3x4;
		return(E_SUCCESS);

		case EResGET_DEFAULT_VALUE_STRING:
			*((const char**)PDPtr) = _float3x4_Default;
		return(E_SUCCESS);
	}
	return(E_NOTHING_TO_DO);
}


static int _VertexProgramInit(E3dStageProgram* PVertexProgram)
{
	E3dCgVertexProgram*  LVertexProgram = (E3dCgVertexProgram*)PVertexProgram;

	LVertexProgram->Param_VtA_Color = E3dSLParameterNONE;
	LVertexProgram->Param_VtA_Tangent = E3dSLParameterNONE;

	{
		EIndex  LI = 0;for(;LI < E3dSLMAX_NUM_OF_TEXCOORDS;LI++)
		{
			LVertexProgram->Param_VtU_TextureMapperMatrix[LI] = E3dSLParameterNONE;
			LVertexProgram->Param_VtA_TextureTangent[LI] = E3dSLParameterNONE;
		}
	}

	{ EIndex  LI = 0;for(;LI < E3dSLMAX_NUM_OF_LIGHTS;LI++) LVertexProgram->Param_VtU_ShadowMapProjMatrix[LI] = E3dSLParameterNONE; }
	{ EIndex  LI = 0;for(;LI < E3dSLMAX_NUM_OF_ARRAY_PARAMETERS;LI++) LVertexProgram->Param_VtA_Custom[LI] = E3dSLParameterNONE; }

//printf("%s() %s:%d  [%s%s%s]  %p %p\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PMaterial->Name, ECodeNormal, PVertexProgram, LVertexProgram->Param_VtU_ModelViewProjectionMatrix);fflush(stdout);

if(LVertexProgram->Param_VtU_ModelViewProjectionMatrix) assert(0);

	LVertexProgram->Param_VtU_ModelViewProjectionMatrix = E3dSLParameterNONE;
	LVertexProgram->Param_VtU_EyeLocalPosition = E3dSLParameterNONE;
	LVertexProgram->Param_VtU_LocalToWorldMatrix = E3dSLParameterNONE;
	LVertexProgram->Param_VtU_LocalToWorldUnitVectorMatrix = E3dSLParameterNONE;
	LVertexProgram->Param_VtU_ModelViewMatrix = E3dSLParameterNONE;
	{ EIndex  LI = 0;for(;LI < LVertexProgram->NumOfResourceParameters;LI++) LVertexProgram->ResourceParameters[LI] = E3dSLParameterNONE; }

//printf("%s() %s:%d  %sUNUSED CODE%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
	return(E_SUCCESS);
}


static void _VertexProgramFree(E3dStageProgram* PPrg)
{
	E3dCgVertexProgram*  LProgram = (E3dCgVertexProgram*)PPrg;

	if(LProgram->RefCnt > 0) LProgram->RefCnt -= 1;
	if(LProgram->RefCnt > 0) return;

	if(LProgram->Source && !LProgram->SourceStatic) EFree(LProgram->Source);
	if(LProgram->ResourceParameters && !LProgram->ResourceParametersStatic) EFree(LProgram->ResourceParameters);

	if(LProgram->Program) cgDestroyProgram(LProgram->Program);

	EFree(LProgram);
}

static const char*  _VertexHeader=
"\
#define float_s                float\n\
#define float2_s               float2\n\
#define float3_s               float3\n\
#define float4_s               float4\n\
#define normalize_float3(p)    normalize(p)\n\
#define E3dFloat3x3            float3x3\n\
#define E3dFloat4x4            float4x4\n\
#define E3dCoord2              float2\n\
#define E3dCoord3              float3\n\
#define E3dCoord4              float4\n\
#define E3dMatrix_Mul(pM, pV)  mul(pM, pV)\n\
#define Pconst                 const\n\
#define Lconst                 const\n\
#define E_Min(p0, p1)          min(p0, p1)\n\
#define E_Max(p0, p1)          max(p0, p1)\n\
#define E_Dot3_vv(p0, p1)      dot(p0, p1)\n\
#define E3dPI                  3.14159265358979\n\
";

static const char*  _BuiltIn_VertexNormalInParamName   = "NORMAL";
static const char*  _BuiltIn_VertexTexCoordInParamName = "TEXCOORD";

static const char*  _Fragment_OutColorName = "OutColor";

static const char*  _VtxUParamQualifier = "uniform";
static const char*  _VtxUParamPrefix = "VtU_";
static const char*  _VtxFParamPrefix = "IN.";

static const char*  _FrgUParamQualifier = "uniform";
static const char*  _FrgUParamPrefix = "FgU_";

static const char*  _Param_DefFormatString = "uniform %s %s";
static const char*  _Sampler2D_DefFormatString = "uniform sampler2D %s";
static const char*  _FragmentHeader=
"\
#define float_s                  float\n\
#define float2_s                 float2\n\
#define float3_s                 float3\n\
#define float4_s                 float4\n\
#define normalize_float3(p)      normalize(p)\n\
#define E3dFloat3x3              float3x3\n\
#define E3dCoord2                float2\n\
#define E3dCoord3                float3\n\
#define E3dCoord4                float4\n\
#define E3dSamplerCube           samplerCUBE\n\
#define E3dTextureCube           texCUBE\n\
#define E3dHalf                  half\n\
#define E3dHalf2                 half2\n\
#define E3dHalf3                 half3\n\
#define E3dHalf4                 half4\n\
#define Pconst                   const\n\
#define Lconst                   const\n\
#define Gstatic\n\
#define Gstatic_inline\n\
#define Pstatic\n\
#define Lstatic\n\
#define E3d_Abs(p)                  abs(p)\n\
#define E_Add3i_vv(pD, p0, p1)    pD = p0 + p1\n\
#define E_Mul3i_vs(pD, p0, p1)    pD = p0 * p1\n\
#define E3d_Length3_v(p)            length(p)\n\
#define E3d_Normalize3_v(p)         p = normalize(p)\n\
#define E_Dot3_vv(p0, p1)         dot(p0, p1)\n\
#define E_Smoothstep_f(p0, p1, pT)  smoothstep(p0, p1, pT)\n\
#define E3dMatrix_Mul(pM, pV)       mul(pM, pV)\n\
#define E_Min(p0, p1)               min(p0, p1)\n\
#define E_Max(p0, p1)               max(p0, p1)\n\
#define LightingFloat3_t         float3\n\
#define LightingColor_t          half4\n\
#define TextureColor_t           half4\n\
#define OutColor_t               half4\n\
#define OutColor_t_cast          half4\n\
#define TextureBlendingFactor_t  half\n\
#define E3dTexture2D(pTexture, pCoordinates)  tex2D(pTexture, pCoordinates)\n\
\n\
#define E3dPI  3.14159265358979\n\
#define E_Neg3_v(pA)       -(pA)\n\
#define E_Sub3_vv(pA, pB)  (pA - pB)\n\
#define E_Swap_f(pA, pB) do { Lconst float  lTmp = pA;pA = pB;pB = lTmp; } while(0)\n\
#include <cstdlib>\n\n\
";

static const char*  _FragmentFunctionPrefix = "void";
static const char*  _FragmentEntryPointName = "main";


//========================================================================================
// Configure EQUINOX-3D's universal shader code-generator to generate Cg shaders
//========================================================================================
static void E3dSL_CodeGeneratorInit(E3dRendererClass* PRendererClass)
{
	E3dShaderDevice*  LShaderDevice = PRendererClass->ShaderDevice;if(LShaderDevice->CodeGeneratorInitialized) return;

	LShaderDevice->ShadowMappingAutoColor = TRUE;

	LShaderDevice->LanguageName = EStrDup("Cg");
	LShaderDevice->LanguageVersionString = EStrDup("31");

	LShaderDevice->ParametersGlobal = FALSE;  // Cg can use structs as function args for shader parameters
	LShaderDevice->SupportsConstForLocalVariables = TRUE;

	LShaderDevice->MaxNumOfTextures = E_Min(E3dSLMAX_NUM_OF_2DTEXTURES, EpGL_MAX_TEXTURE_IMAGE_UNITS);

	LShaderDevice->VertexHeader = _VertexHeader;

	LShaderDevice->FragmentHeader = _FragmentHeader;
	LShaderDevice->FragmentFunctionPrefix = _FragmentFunctionPrefix;
	LShaderDevice->FragmentEntryPointName = _FragmentEntryPointName;

	LShaderDevice->BuiltIn_VertexNormalInParamName   = _BuiltIn_VertexNormalInParamName;
	LShaderDevice->BuiltIn_VertexTexCoordInParamName = _BuiltIn_VertexTexCoordInParamName;

	LShaderDevice->BuiltIn_FragmentOutColorName = _Fragment_OutColorName;

	LShaderDevice->RT_sampler2D       = _RT_sampler2D;
	LShaderDevice->RT_sampler2DShadow = _RT_sampler2D;
	LShaderDevice->RT_sampler3D   = _RT_sampler3D;
	LShaderDevice->RT_samplerCUBE = _RT_samplerCUBE;
	LShaderDevice->RT_float3x3    = _RT_float3x3;
	LShaderDevice->RT_float3x4    = _RT_float3x4;

	EList_Free_s(LShaderDevice->NormalMapPixelFormats);
	{ EpPixelFormat*  LV = EList_StructAdd_s(LShaderDevice->NormalMapPixelFormats);EpPixelFormat_Set(LV,  EpRGB, 4, EResUINT8); }
	{ EpPixelFormat*  LV = EList_StructAdd_s(LShaderDevice->NormalMapPixelFormats);EpPixelFormat_Set(LV,  EpRGB, 3, EResUINT16); }
	{ EpPixelFormat*  LV = EList_StructAdd_s(LShaderDevice->NormalMapPixelFormats);EpPixelFormat_Set(LV,  EpRGB, 3, EResINT16); }
	{ EpPixelFormat*  LV = EList_StructAdd_s(LShaderDevice->NormalMapPixelFormats);EpPixelFormat_Set(LV,  EpRG, 2, EResINT16); }

	LShaderDevice->HasSwizzle = TRUE;
	LShaderDevice->XName = 'x';LShaderDevice->YName = 'y';LShaderDevice->ZName = 'z';LShaderDevice->WName = 'w';
	LShaderDevice->RName = 'r';LShaderDevice->GName = 'g';LShaderDevice->BName = 'b';LShaderDevice->AName = 'a';

	LShaderDevice->VtxFParamPrefix = _VtxFParamPrefix;

	LShaderDevice->VtxUParamQualifier = _VtxUParamQualifier;
	LShaderDevice->VtxUParamPrefix = _VtxUParamPrefix;

	LShaderDevice->FrgUParamQualifier = _FrgUParamQualifier;
	LShaderDevice->FrgUParamPrefix = _FrgUParamPrefix;

	LShaderDevice->Param_DefFormatString = _Param_DefFormatString;
	LShaderDevice->Sampler2D_DefFormatString = _Sampler2D_DefFormatString;
	LShaderDevice->Sampler2DShadow_DefFormatString = _Sampler2D_DefFormatString;
	LShaderDevice->Sampler_PassCoordinatesAsVector = TRUE;
	LShaderDevice->VectorOperators = TRUE;

	LShaderDevice->AccumulateLightPerShadeCall = TRUE;

	LShaderDevice->CodeGeneratorInitialized = TRUE;
	PRendererClass->ShaderDevice = LShaderDevice;
}


//========================================
// Entry point of the plugin
//========================================
int E_PluginInit(EPlugin* PPlugin)
{
	_Plugin = PPlugin;

// Register common superclass for programmable-GPU renderers.
//
	E3dGPURenderer_ClassRegister();

	E3dRendererClass*  LRendererClass = NULL;

	{
		E3dRendererClass  LClass =
		{
			.SuperClass=       E3dPGPURenderer_class,
			.Name=             "Cg",
			.StructSize=       sizeof(E3dThisRenderer),
			.Resources=        EListS_s(_Resources),

			.Init=             _Init,
			.Free=             _Free,

			.ClassFree=        E3dSRenderer_ClassFree,

			.Update=           _Update,
			.Render=           _Render,
			.BindMaterial=     _BindMaterial,
			.UnbindMaterial=   _UnbindMaterial,
			.ShaderClassFree=  _ShaderClassFree,

			.GUI=              E3dPGPURenderer_GUI,
		};

		LRendererClass = E3dRenderer_ClassRegister(&LClass);
	}

	E3dSL_ClearDebugRec();

	if(LRendererClass)
	{
		_RendererClass = LRendererClass;

		LRendererClass->ShaderClassRendererBindingSize = sizeof(E3dShaderClassRendererBinding);

		E3dShaderDevice*  LShaderDevice;ENewClr(LShaderDevice);
		LRendererClass->ShaderDevice = LShaderDevice;

		LRendererClass->ShaderVariantSize = sizeof(E3dThisShaderVariant);
		LShaderDevice->ShaderVariantInit = _ShaderVariantInit;

		LRendererClass->Info = _Info;

		LShaderDevice->VertexProgramInit = _VertexProgramInit;
		LShaderDevice->VertexProgramFree = _VertexProgramFree;

		LShaderDevice->VertexProgramSize = sizeof(E3dCgVertexProgram);

		LShaderDevice->GeometryProgramSize = sizeof(E3dCgGeometryProgram);

		LShaderDevice->FragmentProgramSize = sizeof(E3dCgFragmentProgram);
		LShaderDevice->ShaderCheckNeededTerms = E3dSL_ShaderCheckNeededTerms;


// Register the built-in shaders with this Renderer
//
		_UnlitShaderClass = E3dShader_ClassByName("Constant");if(_UnlitShaderClass)
		{
			E3dShaderClassRendererBinding  LDescriptor=
			{
				.ShaderBindingStructSize=  sizeof(E3dShaderCgBinding),
				.Resources=                E3dSL_ConstantResources,
			};
			E3dShaderClassRendererBinding*  LShaderClassBinding = E3dShader_ClassAddRenderer(_UnlitShaderClass, LRendererClass, &LDescriptor);if(LShaderClassBinding)
			{
				LShaderClassBinding->SourceCore = (char*)Material_Constant_core_sl;
				LShaderClassBinding->StagesUsed = E3dShaderStageVERTEX_AUTO|E3dShaderStageFRAGMENT_AUTO;
			}
		}

		_LambertShaderClass = E3dShader_ClassByName("Lambert");if(_LambertShaderClass)
		{
			E3dShaderClassRendererBinding  LDescriptor=
			{
				.ShaderBindingStructSize=  sizeof(E3dShaderCgBinding),
				.Resources=                E3dSL_LambertResources,
			};
			E3dShaderClassRendererBinding*  LShaderClassBinding = E3dShader_ClassAddRenderer(_LambertShaderClass, LRendererClass, &LDescriptor);if(LShaderClassBinding)
			{
				LShaderClassBinding->SourceCore = (char*)Material_Lambert_core_sl;
				LShaderClassBinding->StagesUsed = E3dShaderStageVERTEX_AUTO|E3dShaderStageFRAGMENT_AUTO;
			}
		}

		_PhongShaderClass = E3dShader_ClassByName("Phong");if(_PhongShaderClass)
		{
			E3dShaderClassRendererBinding  LDescriptor=
			{
				.ShaderBindingStructSize=  sizeof(E3dShaderCgBinding),
				.Resources=                E3dSL_PhongResources,
			};
			E3dShaderClassRendererBinding*  LShaderClassBinding = E3dShader_ClassAddRenderer(_PhongShaderClass, LRendererClass, &LDescriptor);if(LShaderClassBinding)
			{
				LShaderClassBinding->SourceCore = (char*)Material_Phong_core_sl;
				LShaderClassBinding->StagesUsed = E3dShaderStageVERTEX_AUTO|E3dShaderStageFRAGMENT_AUTO;

				_FeatureCg = E3dFeature_Register(E3dFEATURE_HARDWARE_RENDERER, "Cg shaders");
			}
		}
	}

	return(E_SUCCESS);
}


//========================================
// Exit method of the plugin
//========================================
int E_PluginExit(const EBool PExiting)
{
	if(_RendererClass)
	{
//printf("%s() %s:%d  %s%s%s Instances %lld\n", __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, _RendererClass->Name, ECodeNormal,  (long long)_RendererClass->Instances.Count);fflush(stdout);
		if(PExiting)
		{
			if(_RendererClass->Instances.Count > 0) return(E_BUSY);
		}

		_RendererClass->SuperClass = NULL;    // The SuperClass (E3dPGPURenderer_class) is also definied in a plugin and we'll deactivate that too, so we need to NULL this out
		E3dRenderer_ClassDeactivate(_RendererClass);_RendererClass = NULL;
	}

	if(E3dPGPURenderer_class)
	{
//printf("%s() %s:%d  %s%p%s:%s%s%s  ClassFlags  %04x  Instances %lld  ActiveSubClasses %lld\n", __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, E3dPGPURenderer_class, ECodeNormal,  ECodeGreen, E3dPGPURenderer_class->Name, ECodeNormal,  E3dPGPURenderer_class->ClassFlags,  (long long)E3dPGPURenderer_class->Instances.Count, (long long)E3dRenderer_ClassActiveSubClasses(E3dPGPURenderer_class));fflush(stdout);
		if(E3dRenderer_ClassActiveSubClasses(E3dPGPURenderer_class) < 1) { E3dRenderer_ClassDeactivate(E3dPGPURenderer_class);E3dPGPURenderer_class = NULL; }
	}

	if(E3dCg_ShaderContext) { cgDestroyContext(E3dCg_ShaderContext);E3dCg_ShaderContext = 0; }

	if(_FeatureCg) { E3dFeature_Remove(_FeatureCg);_FeatureCg = NULL; }

	return(E_SUCCESS);
}
