/************************************************************************************************************
	MixRenderer.cpp
	Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

	Author:	Mael Sicsic

	Date:	8/10/99
	Revision History

	when		who			what & why
	=========== ===========	=============================================================
	08/20/99	cph			Put in Extension3DCleanup() methods

************************************************************************************************************/


#include "math.h"
#include "MixRenderer.h"
#include "MCCountedPtrHelper.h"
#include "I3DShUtilities.h"
#include "I3DShScene.h"
#include "ToolBox.h"
#include "PublicUtilities.h"
#include "IShRasterLayerUtilities.h"
#include "I3DShRenderable.h"
#include "I3DShEnvironment.h"

const MCGUID CLSID_MixRenderer(R_CLSID_MixRenderer);


void Extension3DInit(IMCUnknown* utilities)

{
	// Perform your dll initialization here
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}


TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the WireRenderer
	TBasicUnknown* res = NULL;

	if (classId == CLSID_MixRenderer) res = new MixRenderer;

	MCAssert(res);
	return res;
}


// Constructor of the renderer
MixRenderer::MixRenderer()
{
	fRendEnv.fAmbient.R = 0.5f;	
	fRendEnv.fAmbient.G = 0.5f;
	fRendEnv.fAmbient.B = 0.5f;
	fRendEnv.fAtmosphere = NULL;
	fRendEnv.fBackdrop = NULL;
	fRendEnv.fBackground = NULL;
	fCamera = NULL;
	fDrawingArea.left = 0;
	fDrawingArea.right = 0;
	fDrawingArea.top = 0;
	fDrawingArea.bottom = 0;
	fImageArea.left = 0;
	fImageArea.right = 0;
	fImageArea.top = 0;
	fImageArea.bottom = 0;
	fMode = 0;
	fOffscrOffset[0] = 0.0f;
	fOffscrOffset[1] = 0.0f;
	fRendEnv.fTree = NULL;
	fZoom[0] = 1.0f;
	fZoom[1] = 1.0f;
	fPMap.frontier = 320;
}





//*****************************************************************

//				BasicDataExchanger Methods

//*****************************************************************

void* MixRenderer::GetExtensionDataBuffer()

{

	return &fPMap;

}



int16 MixRenderer::GetResID()

{

	return 134;

}	//this is the ID in the resource file (typically .DTA)





//*****************************************************************

//				I3DExRenderer Methods

//*****************************************************************



// specifies which tree needs to be rendered (generally the universe)

MCCOMErr MixRenderer::SetTreeTop(I3DShGroup* treeTop)

{

	fRendEnv.fTree = treeTop;

	return MC_S_OK;

}



// specifies the rendering camera

MCCOMErr MixRenderer::SetCamera (I3DShCamera* camera)

{

	fCamera = camera;

	return MC_S_OK;

} 



// gives the background, backdrop & atmospheric shader.

MCCOMErr MixRenderer::SetEnvironment (I3DShEnvironment* environment)

{

	if (!environment || environment->HasBackground()) fRendEnv.fBackground=environment;

	if (!environment || environment->HasBackdrop()) fRendEnv.fBackdrop=environment;

	if (!environment || environment->HasAtmosphere()) fRendEnv.fAtmosphere=environment;

	return MC_S_OK;

}	



// sets the ambient light color

MCCOMErr MixRenderer::SetAmbientLight (const TMCColorRGB &ambientColor)

{

	fRendEnv.fAmbient = ambientColor;

	return MC_S_OK;

}



// used to specify which buffers owned by the component could be freed in case Kwak would lack memory

MCCOMErr MixRenderer::Dehydrate (int16 level)

{

	return MC_S_OK;

}



// this corresponds to an option in Scene Settings/Output

uint32 MixRenderer::GetRenderingTime ()

{

	return 0;

}



// multiple frames for video compositing (option in Scene Settings/Renderer)

MCCOMErr MixRenderer::SetFieldRenderingData (int32 useFieldRendering,int16 firstFrame)

{

	return MC_S_OK;

}





void MixRenderer::InitZbuffer()

{

	//Seems very clear, we initialize the Zbuffer

	fZbuffer = new real[fImageHeight*fImageWidth];

	

	for ( int i=0; i<fImageHeight*fImageWidth; i++)

		fZbuffer[i] = FPOSINF;

}





void MixRenderer::AllocMem()

{

	//According to the shading flags we instanciate only the requested items array

	if ( rightX == NULL)

	{

		rightX = new real[fImageHeight];

		leftX = new real[fImageHeight];

		

		rightZ = new real[fImageHeight];

		leftZ = new real[fImageHeight];

		

		rightGlobalPosX = new real[fImageHeight];

		leftGlobalPosX = new real[fImageHeight];

		rightGlobalPosY = new real[fImageHeight];

		leftGlobalPosY = new real[fImageHeight];

		rightGlobalPosZ = new real[fImageHeight];

		leftGlobalPosZ = new real[fImageHeight];

		

		rightGlobalNorX = new real[fImageHeight];

		leftGlobalNorX = new real[fImageHeight];

		rightGlobalNorY = new real[fImageHeight];

		leftGlobalNorY = new real[fImageHeight];

		rightGlobalNorZ = new real[fImageHeight];

		leftGlobalNorZ = new real[fImageHeight];

	}

	

	if (( fFlags.fNeedsUV == (uint)true ) && ( rightU == NULL))

	{

		rightU = new real[fImageHeight];

		leftU = new real[fImageHeight];

		rightV = new real[fImageHeight];

		leftV = new real[fImageHeight];

	}

	

	if (( fFlags.fNeedsPointLoc == (uint)true ) && ( rightLocPosX == NULL ))

	{

		rightLocPosX = new real[fImageHeight];

		leftLocPosX = new real[fImageHeight];

		rightLocPosY = new real[fImageHeight];

		leftLocPosY = new real[fImageHeight];

		rightLocPosZ = new real[fImageHeight];

		leftLocPosZ = new real[fImageHeight];

	}

	

	if (( fFlags.fNeedsNormalLoc == (uint)true ) && ( rightLocNorX == NULL )) 

	{

		rightLocNorX = new real[fImageHeight];

		leftLocNorX = new real[fImageHeight];

		rightLocNorY = new real[fImageHeight];

		leftLocNorY = new real[fImageHeight];

		rightLocNorZ = new real[fImageHeight];

		leftLocNorZ = new real[fImageHeight];

	}

}





void MixRenderer::ReleaseMem()

{
	//According to the shading flags we instanciate only the requested items array
	if ( leftX != NULL) {delete []leftX; leftX=NULL;}
	if ( leftZ != NULL) {delete []leftZ; leftZ=NULL;}
	if ( leftU != NULL) {delete []leftU; leftU=NULL;}
	if ( leftV != NULL) {delete []leftV; leftV=NULL;}
	if ( leftGlobalPosX != NULL) {delete []leftGlobalPosX; leftGlobalPosX=NULL;}
	if ( leftGlobalPosY != NULL) {delete []leftGlobalPosY; leftGlobalPosY=NULL;}
	if ( leftGlobalPosZ != NULL) {delete []leftGlobalPosZ; leftGlobalPosZ=NULL;}
	if ( leftGlobalNorX != NULL) {delete []leftGlobalNorX; leftGlobalNorX=NULL;}
	if ( leftGlobalNorY != NULL) {delete []leftGlobalNorY; leftGlobalNorY=NULL;}
	if ( leftGlobalNorZ != NULL) {delete []leftGlobalNorZ; leftGlobalNorZ=NULL;}
	if ( leftLocPosX != NULL) {delete []leftLocPosX; leftLocPosX=NULL;}
	if ( leftLocPosY != NULL) {delete []leftLocPosY; leftLocPosY=NULL;}
	if ( leftLocPosZ != NULL) {delete []leftLocPosZ; leftLocPosZ=NULL;}
	if ( leftLocNorX != NULL) {delete []leftLocNorX; leftLocNorX=NULL;}
	if ( leftLocNorY != NULL) {delete []leftLocNorY; leftLocNorY=NULL;}
	if ( leftLocNorZ != NULL) {delete []leftLocNorZ; leftLocNorZ=NULL;}

	if ( rightX != NULL) {delete []rightX; rightX=NULL;}
	if ( rightZ != NULL) {delete []rightZ; rightZ=NULL;}
	if ( rightU != NULL) {delete []rightU; rightU=NULL;}
	if ( rightV != NULL) {delete []rightV; rightV=NULL;}
	if ( rightGlobalPosX != NULL) {delete []rightGlobalPosX; rightGlobalPosX=NULL;}
	if ( rightGlobalPosY != NULL) {delete []rightGlobalPosY; rightGlobalPosY=NULL;}
	if ( rightGlobalPosZ != NULL) {delete []rightGlobalPosZ; rightGlobalPosZ=NULL;}
	if ( rightGlobalNorX != NULL) {delete []rightGlobalNorX; rightGlobalNorX=NULL;}
	if ( rightGlobalNorY != NULL) {delete []rightGlobalNorY; rightGlobalNorY=NULL;}
	if ( rightGlobalNorZ != NULL) {delete []rightGlobalNorZ; rightGlobalNorZ=NULL;}
	if ( rightLocPosX != NULL) {delete []rightLocPosX; rightLocPosX=NULL;}
	if ( rightLocPosY != NULL) {delete []rightLocPosY; rightLocPosY=NULL;}
	if ( rightLocPosZ != NULL) {delete []rightLocPosZ; rightLocPosZ=NULL;}
	if ( rightLocNorX != NULL) {delete []rightLocNorX; rightLocNorX=NULL;}
	if ( rightLocNorY != NULL) {delete []rightLocNorY; rightLocNorY=NULL;}
	if ( rightLocNorZ != NULL) {delete []rightLocNorZ; rightLocNorZ=NULL;}
}



void MixRenderer::GetLights()
{
	TMCCountedPtr<I3DShTreeElement> aTree;

	TMCCountedPtr<I3DShScene> scene;

	fRendEnv.fTree->QueryInterface(IID_I3DShTreeElement,(void **)&aTree);

	ThrowIfNil(aTree);

	aTree->GetScene(&scene);



	fNbLights = scene->GetLightsourcesCount();



	for (int32 index=0; index<fNbLights; index++)

	{

		TMCCountedPtr<I3DShLightsource> light;

		scene->GetLightsourceByIndex(&light,index);

		fLights[index] = light;

	}

}





void MixRenderer::FreeLights()

{

//	delete[] fLights;

}







//*****************************************************************

//				WireRenderer Methods

//*****************************************************************



// This method is called by the the PrepareDraw method. It allows us to draw the image in our buffer

void MixRenderer::RenderScene(const TMCRect* area)

{

	TTransform3D G2C ;		//Transformation from Global to Camera 

	TTransform3D L2C ;		//Transformation from Local to Camera

	TTransform3D L2G ;		//Transformation from Local to Global

	

	TMCCountedPtr<FacetMesh> amesh;	// pointer to IFacetMesh interface



	TFacet3D		facet;

	TMCColorRGBA	color, notused;

	int32			theKind=0;	//Used to stock the kind of an instance

	

	

	fRendEnv.fTree->QueryInterface(IID_I3DShTreeElement,(void**)&fTree);

	ThrowIfNil(fTree);

	fTree->BeginGetRenderables(fInstances);

	

	

	fCamera->GetGlobalToCameraTransform(&G2C) ; //We get transformation from Global to Camera,

	//actually in Screen Coord 

	

	FacetMeshFacetIterator* facetIterator = new FacetMeshFacetIterator;

	TRenderableAndTfmArray::const_iterator	iter = fInstances->Begin();

	

	for (const TRenderableAndTfm* current = iter.First(); iter.More(); current = iter.Next())

	{

		theKind = current->fInstance->GetInstanceKind();

		if ((theKind == 3) || (theKind == 4)) 

			continue;

		

		L2G = current->fT ;		//In InstanceAndTransform fT is not a Translation as we may imagine

								//but a AFFINETRANSFORM cad Translation + Rotation

								//we get transformation from Local to Global system

		

		//Then we get our complete transformation from local to Camera Coordinate

		L2C = G2C*L2G;

		

		current->fInstance->GetShadingFlags(fFlags); //We get flags from shaders

		current->fInstance->GetIndex(fIndex);

		

		fParametricMapping = true;

		

		AllocMem(); //We allocate the right buffers for the right items

		

		current->fInstance->GetMainColors(color,notused);

		amesh= current->fInstance->GetRenderingFacetMesh() ;		//get a pointer to IFacetMesh interface

		

		facetIterator->Initialize( amesh ) ;					//Initialization for a using of a facet iterator

		

		for ( facetIterator->First(); facetIterator->More(); facetIterator->Next())

		{ // browsing into facets list

			facet = facetIterator->GetFacet();

			Rasterize( facet, L2G, L2C, current->fInstance );			

		} 

		

		//We're now filling the pixels which don't refer to any object and thus have been left aside

		for (int y = 0; y < fImageHeight ; y++)

		{

			TMCColorRGBA color;

			int line = y*fImageWidth;

			TVector2 screenXY;

			screenXY[1] = (real)y;

			for (int x=0; x<fImageWidth; x++)

			{

				if (fZbuffer[line+x] == FPOSINF)

				{

					color.R = 0;

					color.G = 0;

					color.B = 0;

					screenXY[0] = (real)x;

					BackgroundColor(screenXY,color);

					PixelSet(x,y,&color);

				}

			}

		}				

		ReleaseMem();

	}	

	delete facetIterator;

	fTree->EndGetRenderables();

	fTree = NULL;

	fRendEnv.fTree = NULL;

}





// This method is called just at the beginning of the render

MCCOMErr MixRenderer::PrepareDraw (const TMCPoint& size,const TBBox2D& uvBox,const TBBox2D& productionFrame, boolean (*callback) (int16 vv, void *priv),void* priv)
{	
	int scrBufferSize = fImageHeight*fImageWidth;


	//In order to use the powerful Raydream shader we are supposed to interpolate the right items 
	//at the right time,it's the only difficulty in this code.
	//Then, we have chosen one temporary array per item


	rightX = NULL; leftX = NULL;
	rightZ = NULL; leftZ = NULL;

	rightGlobalPosX = NULL; leftGlobalPosX = NULL;
	rightGlobalPosY = NULL; leftGlobalPosY = NULL;
	rightGlobalPosZ = NULL; leftGlobalPosZ = NULL;


	rightGlobalNorX = NULL; leftGlobalNorX = NULL;
	rightGlobalNorY = NULL; leftGlobalNorY = NULL;
	rightGlobalNorZ = NULL; leftGlobalNorZ = NULL;

	rightLocPosX = NULL; leftLocPosX = NULL;
	rightLocPosY = NULL; leftLocPosY = NULL;
	rightLocPosZ = NULL; leftLocPosZ = NULL;

	rightLocNorX = NULL; leftLocNorX = NULL;
	rightLocNorY = NULL; leftLocNorY = NULL;
	rightLocNorZ = NULL; leftLocNorZ = NULL;

	rightU = NULL; leftU = NULL;
	rightV = NULL; leftV = NULL;

	fFrontier = fPMap.frontier;

	fImageArea = TMCRect(0,0,size.x,size.y);

	fBuffer.red   = NULL;
	fBuffer.green = NULL;
	fBuffer.blue  = NULL;

	fZbuffer  = NULL;

	fImageWidth  = size.x;	// We get the dimensions of
	fImageHeight = size.y;	// the image to be drawn
	fDepth = 8;				// For compatibility with Put32

	fZoom[0] = size.x/2;

	fZoom[1] = size.y/2;



	fZoom[0] = real(size.x) / (uvBox.fMax[0] - uvBox.fMin[0]);

	fZoom[1] = real(size.y) / (uvBox.fMax[1] - uvBox.fMin[1]);



	fOffscrOffset[0] = real(size.x) * (-uvBox.fMin[0] / (uvBox.fMax[0] - uvBox.fMin[0]));

	fOffscrOffset[1] = real(size.y) * (uvBox.fMax[1] / (uvBox.fMax[1] - uvBox.fMin[1]));



	fRendEnv.fuvMinMax = productionFrame;

	

	GetLights();

	InitZbuffer();


	// init the screen buffer

	fBuffer.red   = new uint8[size.x*size.y];
	fBuffer.green = new uint8[size.x*size.y];
	fBuffer.blue  = new uint8[size.x*size.y];

	for (int32 i=0; i<fImageHeight*fImageWidth; i++)
	{
		fBuffer.red[i]=0;
		fBuffer.green[i]=120;
		fBuffer.blue[i]=120;
	}

	RenderScene(&fImageArea);

	return MC_S_OK;

}

void MixRenderer::GetTileRenderer(I3DExTileRenderer** tileRenderer)
{
	TMCCountedGetHelper<I3DExTileRenderer> result(tileRenderer);
	result = this;
}

void MixRenderer::Init(const TMCRect& rect,const TBBox2D& uvBox)
{
	fCurrentTileRect = rect;
	fCurrentUVBox	 = uvBox;
}

boolean MixRenderer::GetNextSubTile(TMCRect& rect)
{
	return false;
}

void MixRenderer::RenderSubTile(RTData& pixels)
{
	MCNotify("Bad Call!"); // should never be called
}


// This method allows us to fill the RTData. Then we have our image to the screen
void MixRenderer::FinishRender(const RTData& pixels)
{
	//Be careful a slight difference exists between Camera Coord and Screen Coord. Basically,
	//both are the camera view, the only change is for Camera coord view axis is -z whereas for
	//screen coord view axis is y for further details see DataBase Overview in CookBook
	//For the following, Camera means Screen Coord

	fDrawingArea = fCurrentTileRect;
	BlocCopy(&pixels);
}


// drawing time is over, we have to delete temporary components
MCCOMErr MixRenderer::FinishDraw ()

{
	if (fBuffer.red)	delete [] fBuffer.red;
	if (fBuffer.green)	delete [] fBuffer.green;
	if (fBuffer.blue)	delete [] fBuffer.blue;

	if (fZbuffer!=NULL) delete [] fZbuffer;

	ReleaseMem();

	return MC_S_OK;
}	



// This method is called by the DrawRect call. It copys a tile of our buffer to the RTData

void MixRenderer::BlocCopy(const RTData* pixels)

{
	int32 i,j;

	int32 height=fDrawingArea.bottom-fDrawingArea.top;
	int32 width	=fDrawingArea.right-fDrawingArea.left;

	uint8 *red		= pixels->red;
	uint8 *green	= pixels->green;
	uint8 *blue		= pixels->blue;
	uint8 *alpha	= pixels->alpha;

	int32 columnStep = 1;

	int32 toNextLine = pixels->rowBytes;

	for (j=0; j<height; j++)
	{
		for (i=0; i<width; i++)
		{
			red  [i*columnStep]	= fBuffer.red[i+fDrawingArea.left + (j+fDrawingArea.top)*fImageWidth];
			green[i*columnStep]	= fBuffer.green[i+fDrawingArea.left + (j+fDrawingArea.top)*fImageWidth];
			blue [i*columnStep]	= fBuffer.blue[i+fDrawingArea.left + (j+fDrawingArea.top)*fImageWidth];
		}
		if (alpha)
		{
			for (i=0; i<width; i++)
			{
				alpha[i*columnStep] = 255;
			}
			alpha += toNextLine;
		}
		red		+=toNextLine;
		green	+=toNextLine;
		blue	+=toNextLine;
	}
}


// This method is used to draw the facets in our buffer

void MixRenderer::Rasterize(TFacet3D facet, TTransform3D L2G, TTransform3D L2S, I3DShInstance* CurrentInstance )

{	

	TVector3		screenCoordPoint[3];

	TMCColorRGBA	color, notused;

	ShadingIn		myShadingIn;

	TVector2		screenVertex[3];

	real			zOut;

	int				numVertices = 3;

	

	

	//We transform local Vertex to Vertex in Camera view,i.e. in Screen Coord using L2S

	MixTransformPoint(screenCoordPoint[0], L2S, facet.fVertices[0].fVertex);

	MixTransformPoint(screenCoordPoint[1], L2S, facet.fVertices[1].fVertex);

	MixTransformPoint(screenCoordPoint[2], L2S, facet.fVertices[2].fVertex);

	

	

	//We transform vertex in Local to Vertex in Global Coord using L2G

	//Since we use Phong model, we always need normals

	//In that case we need normals and positions in Global coord System

	MixTransformPoint(fGlobalVertex[0].fVertex, L2G, facet.fVertices[0].fVertex);

	MixTransformPoint(fGlobalVertex[1].fVertex, L2G, facet.fVertices[1].fVertex);

	MixTransformPoint(fGlobalVertex[2].fVertex, L2G, facet.fVertices[2].fVertex);

	

	MixTransformVector(fGlobalVertex[0].fNormal, L2G, facet.fVertices[0].fNormal);

	MixTransformVector(fGlobalVertex[1].fNormal, L2G, facet.fVertices[1].fNormal);

	MixTransformVector(fGlobalVertex[2].fNormal, L2G, facet.fVertices[2].fNormal);

	

	//We have to realize that we are supposed to give the right the parameters to the shaders

	//i.e we have to fill a ShadingIn structure in order to get ShadingOut structure. Thus, we can compute the Illumination

	//See Data Stucture Reference in Reference Manual.	

	

	

	myShadingIn.fUVSpaceID = facet.fUVSpace;

	

	//Contrary to others items, we compute IsoU and IsoV for the facet

	//Indeed, others items are computed at the pixel level

	//We only deal with isoU and isoV when the mapping is parametric.

	//If the mapping is projectional, Doshade will compute isoU and isoV

	

	if (( fFlags.fNeedsIsoUV == (uint)true ))

		GetIsoUV (myShadingIn.fIsoU, myShadingIn.fIsoV, facet);

	

	UVSpaceInfo myUVSpaceInfo;

	myShadingIn.fUVInfo = &myUVSpaceInfo;

	

	//We transform Vertex in Screen Coord in Vertex in Screen Space, from 3D to 2D

	boolean result1 = fCamera->Project3DTo2D(&screenCoordPoint[0], &screenVertex[0], &zOut);

	boolean result2 = fCamera->Project3DTo2D(&screenCoordPoint[1], &screenVertex[1], &zOut);

	boolean result3 = fCamera->Project3DTo2D(&screenCoordPoint[2], &screenVertex[2], &zOut);

	

	

	//Before displaying each pixel, we have to apply Zoom transformation, and to center

	fScreenPoint[0][0] = fOffscrOffset[0] + (screenVertex[0][0] * fZoom[0]) ;

	fScreenPoint[0][1] = fOffscrOffset[1] - (screenVertex[0][1] * fZoom[1]) ;

	fScreenPoint[0][2] = -screenCoordPoint[0][2]; //We just get the z coordinate

	

	fScreenPoint[1][0] = fOffscrOffset[0] + (screenVertex[1][0] * fZoom[0]) ;

	fScreenPoint[1][1] = fOffscrOffset[1] - (screenVertex[1][1] * fZoom[1]) ;

	fScreenPoint[1][2] = -screenCoordPoint[1][2]; //We just get the z coordinate

	

	fScreenPoint[2][0] = fOffscrOffset[0] + (screenVertex[2][0] * fZoom[0]) ;

	fScreenPoint[2][1] = fOffscrOffset[1] - (screenVertex[2][1] * fZoom[1]) ;

	fScreenPoint[2][2] = -screenCoordPoint[2][2]; //We just get the z coordinate


	if ( (fScreenPoint[0][0] > fFrontier) && (fScreenPoint[1][0] > fFrontier) && (fScreenPoint[2][0] > fFrontier) )
	{
		FillPoly(facet, CurrentInstance, &myShadingIn, numVertices);
	}

	else if ( (fScreenPoint[0][0] < fFrontier) && (fScreenPoint[1][0] < fFrontier) && (fScreenPoint[2][0] < fFrontier) )

	{

		FillPoly(facet, CurrentInstance, &myShadingIn, numVertices);

		DrawLine((int16)fScreenPoint[0][0], (int16)fScreenPoint[0][1], (int16)fScreenPoint[1][0], (int16)fScreenPoint[1][1], &color, screenCoordPoint[0].z,screenCoordPoint[1].z ) ;

		DrawLine((int16)fScreenPoint[1][0], (int16)fScreenPoint[1][1], (int16)fScreenPoint[2][0], (int16)fScreenPoint[2][1], &color, screenCoordPoint[0].z,screenCoordPoint[1].z ) ;

		DrawLine((int16)fScreenPoint[2][0], (int16)fScreenPoint[2][1], (int16)fScreenPoint[0][0], (int16)fScreenPoint[0][1], &color, screenCoordPoint[0].z,screenCoordPoint[1].z ) ;

	}

	else

	{

		FillPoly(facet, CurrentInstance, &myShadingIn, numVertices);

		DrawLine((int16)fScreenPoint[0][0], (int16)fScreenPoint[0][1], (int16)fScreenPoint[1][0], (int16)fScreenPoint[1][1], &color, screenCoordPoint[0].z,screenCoordPoint[1].z ) ;

		DrawLine((int16)fScreenPoint[1][0], (int16)fScreenPoint[1][1], (int16)fScreenPoint[2][0], (int16)fScreenPoint[2][1], &color, screenCoordPoint[0].z,screenCoordPoint[1].z ) ;

		DrawLine((int16)fScreenPoint[2][0], (int16)fScreenPoint[2][1], (int16)fScreenPoint[0][0], (int16)fScreenPoint[0][1], &color, screenCoordPoint[0].z,screenCoordPoint[1].z ) ;

	}	

}





void MixRenderer::GetRenderStatistics(I3DRenderStatistics** renderstats)

{

	TMCCountedGetHelper<I3DRenderStatistics> result(renderstats);

	result = static_cast<I3DRenderStatistics *>(NULL);

}





// This method allow us to change the value of a pixel.

void MixRenderer::PixelSet (int32 x, int32 y, TMCColorRGBA* color)

{
	TGray16 red(color->red);
	TGray16 green(color->green);
	TGray16 blue(color->blue);

	if ((x>=0)&&(x<fImageWidth)&&(y>=0)&&(y<fImageHeight))

	{
		fBuffer.red[x + fImageWidth*y] = red.To8();
		fBuffer.green[x + fImageWidth*y] = green.To8();
		fBuffer.blue[x + fImageWidth*y] = blue.To8();
	}
}





void MixRenderer::ZBufferSet (int32 x, int32 y, real val)

{

	if ((x>=0)&&(x<fImageWidth)&&(y>=0)&&(y<fImageHeight))

	{

		fZbuffer[x + fImageWidth*y] = val;

	}

}





//Here you have the most famous Drawliner algorithm all over the world : Bresenham

void MixRenderer::DrawLine(int16 x1, int16 y1, int16 x2, int16 y2, TMCColorRGBA* Color, real z1, real z2 )

{

	int16	x,y,xi,yi,i,c,dx,dy;

	real dz,z;

	x=x1;

	y=y1;

	z=z1;



	if (x1 < x2)

		xi=1;

	else 

		xi=-1;

	if (y1 < y2)

		yi=1;

	else

		yi=-1;

	dx=fabs(real (x1-x2));

	dy=fabs(real (y1-y2));

	if (dy > dx)

	{

		dz = (z2-z1)/dy;

		if (dz<0) dz=-dz;

		c=dy >> 1;

		for (i=1;i<dy;i++)

		{

			y=y+yi;

			c=c+dx;

			z+=dz;

			if (c >= dy)

			{

				c=c-dy;

				x=x+xi;

			}

			if ( y >= 0 && y < fImageHeight && x >= 0 && x < fFrontier )

			{

				ZBufferSet(x,y,z);	

				PixelSet(x, y, Color);

			}

		}

	}

	else

	{

		dz = (z2-z1)/dx;

		if (dz<0) dz=-dz;

		c=dx >> 1;

		for (i=1;i<dx;i++)

		{

			x=x+xi;

			c=c+dy;

			z+=dz;

			if (c >= dx)

			{

				c=c-dx;

				y=y+yi;

			}

			if ( y >= 0 && y < fImageHeight && x >= 0 && x < fFrontier )

			{

				ZBufferSet(x,y,z);	

				PixelSet(x, y, Color);

			}

		}

    }

}







//A mere interpolation

void MixRenderer::Interpolate(real A, real B, long y1, long y2, long dy, real *array)

{

	real slope;

	real x;

	

	slope = (A - B)/dy;

	

	if ( dy < 0 ) x = A;

	else x = B;

	

	for (int j = y1 ; j <= y2 ; j++ )

	{

		if ( j >= 0 && j < fImageHeight) array[j] = x; //Clipping y axis in ScreenPixel Space

		x += slope;

	}

}





void MixRenderer::FillPoly(TFacet3D facet, I3DShInstance *CurrentInstance, ShadingIn *myShadingIn, int numVertices)

{

	

	long ymin , ymax;

	long y1,y2, dy;

	ymin = IPOSINF ;

	ymax = INEGINF ;

	

	//Be careful, facets are not oriented, and our polygon-filling needs counterclockwise oriented triangles

	int PointOrder[3] = { 0, 1, 2};

	

	TVector3 res = MixNormalFacet(fScreenPoint[0], fScreenPoint[1], fScreenPoint[2]);

	

	if ( res[2] > 0 )

	{

		PointOrder[1] = 2;

		PointOrder[2] = 1;

	}

	

	//We interpolate along triangle edges, ie we interpolate vertically 

	//We just interpolate items wanted by the RayDream Shader

	//Basically, to interpolate, we use two arrays : one left and one right.

	//If the next vertex (counterclockwisely speaking), is below we use left array

	//else we use right array (See drawing in Cookbook).

	//Roughly speaking, we use bilinear interpolation

	for ( int k = 0 ; k < numVertices ; k++ )

	{

		int i = PointOrder[k];

		y1 = (long)(fScreenPoint[i][1]+0.5);

		int t = PointOrder[(k+1)%numVertices];

		y2 = (long)(fScreenPoint[t][1]+0.5);

		

		if ( y1 > y2 )

		{

			if ( y2 < ymin ) ymin = y2;

			if ( y1 > ymax ) ymax = y1;

			

			dy = y2-y1;

			

			Interpolate(fScreenPoint[t][0], fScreenPoint[i][0], y2, y1, dy, rightX);

			Interpolate(fScreenPoint[t][2], fScreenPoint[i][2], y2, y1, dy, rightZ);

			

			Interpolate(fGlobalVertex[t].fVertex[0], fGlobalVertex[i].fVertex[0], y2, y1, dy, rightGlobalPosX);

			Interpolate(fGlobalVertex[t].fVertex[1], fGlobalVertex[i].fVertex[1], y2, y1, dy, rightGlobalPosY);

			Interpolate(fGlobalVertex[t].fVertex[2], fGlobalVertex[i].fVertex[2], y2, y1, dy, rightGlobalPosZ);

			

			Interpolate(fGlobalVertex[t].fNormal[0], fGlobalVertex[i].fNormal[0], y2, y1, dy, rightGlobalNorX);

			Interpolate(fGlobalVertex[t].fNormal[1], fGlobalVertex[i].fNormal[1], y2, y1, dy, rightGlobalNorY);

			Interpolate(fGlobalVertex[t].fNormal[2], fGlobalVertex[i].fNormal[2], y2, y1, dy, rightGlobalNorZ);

			

			if ( fFlags.fNeedsUV == (uint)true )

			{

				Interpolate(facet.fVertices[t].fUV[0], facet.fVertices[i].fUV[0], y2, y1, dy, rightU);

				Interpolate(facet.fVertices[t].fUV[1], facet.fVertices[i].fUV[1], y2, y1, dy, rightV);

			}

			

			if ( fFlags.fNeedsPointLoc == (uint)true )

			{

				

				Interpolate(facet.fVertices[t].fVertex[0], facet.fVertices[i].fVertex[0], y2, y1, dy, rightLocPosX);

				Interpolate(facet.fVertices[t].fVertex[1], facet.fVertices[i].fVertex[1], y2, y1, dy, rightLocPosY);

				Interpolate(facet.fVertices[t].fVertex[2], facet.fVertices[i].fVertex[2], y2, y1, dy, rightLocPosZ);

			}

			

			if ( fFlags.fNeedsNormalLoc == (uint)true )

			{

				Interpolate(facet.fVertices[t].fNormal[0], facet.fVertices[i].fNormal[0], y2, y1, dy, rightLocNorX);

				Interpolate(facet.fVertices[t].fNormal[1], facet.fVertices[i].fNormal[1], y2, y1, dy, rightLocNorY);

				Interpolate(facet.fVertices[t].fNormal[2], facet.fVertices[i].fNormal[2], y2, y1, dy, rightLocNorZ);

			}		

		}

		

		else if ( y1 < y2 )

		{

			if ( y1 < ymin ) ymin = y1 ;

			if ( y2 > ymax ) ymax = y2 ;

			

			dy = y2 - y1;

			

			Interpolate(fScreenPoint[t][0], fScreenPoint[i][0], y1, y2, dy, leftX);

			Interpolate(fScreenPoint[t][2], fScreenPoint[i][2], y1, y2, dy, leftZ);

			

			Interpolate(fGlobalVertex[t].fVertex[0], fGlobalVertex[i].fVertex[0], y1, y2, dy, leftGlobalPosX);

			Interpolate(fGlobalVertex[t].fVertex[1], fGlobalVertex[i].fVertex[1], y1, y2, dy, leftGlobalPosY);

			Interpolate(fGlobalVertex[t].fVertex[2], fGlobalVertex[i].fVertex[2], y1, y2, dy, leftGlobalPosZ);

			

			Interpolate(fGlobalVertex[t].fNormal[0], fGlobalVertex[i].fNormal[0], y1, y2, dy, leftGlobalNorX);

			Interpolate(fGlobalVertex[t].fNormal[1], fGlobalVertex[i].fNormal[1], y1, y2, dy, leftGlobalNorY);

			Interpolate(fGlobalVertex[t].fNormal[2], fGlobalVertex[i].fNormal[2], y1, y2, dy, leftGlobalNorZ);

			

			

			if ( fFlags.fNeedsUV == (uint)true )

			{

				Interpolate(facet.fVertices[t].fUV[0], facet.fVertices[i].fUV[0], y1, y2, dy, leftU);

				Interpolate(facet.fVertices[t].fUV[1], facet.fVertices[i].fUV[1], y1, y2, dy, leftV);

			}

			

			if ( fFlags.fNeedsPointLoc == (uint)true )

			{

				

				Interpolate(facet.fVertices[t].fVertex[0], facet.fVertices[i].fVertex[0], y1, y2, dy, leftLocPosX);

				Interpolate(facet.fVertices[t].fVertex[1], facet.fVertices[i].fVertex[1], y1, y2, dy, leftLocPosY);

				Interpolate(facet.fVertices[t].fVertex[2], facet.fVertices[i].fVertex[2], y1, y2, dy, leftLocPosZ);

			}

			

			if ( fFlags.fNeedsNormalLoc == (uint)true )

			{

				Interpolate(facet.fVertices[t].fNormal[0], facet.fVertices[i].fNormal[0], y1, y2, dy, leftLocNorX);

				Interpolate(facet.fVertices[t].fNormal[1], facet.fVertices[i].fNormal[1], y1, y2, dy, leftLocNorY);

				Interpolate(facet.fVertices[t].fNormal[2], facet.fVertices[i].fNormal[2], y1, y2, dy, leftLocNorZ);

			}

			

		}

	}

	

	//n-gones filling

	

	real	delta;

	real	z;

	real	deltaZ, deltaGlobalPosX, deltaGlobalPosY, deltaGlobalPosZ, 

		deltaGlobalNorX, deltaGlobalNorY, deltaGlobalNorZ,

		deltaLocPosX, deltaLocPosY, deltaLocPosZ,

		deltaLocNorX, deltaLocNorY, deltaLocNorZ,

		deltaU, deltaV;

	

	real	GlobalPosX, GlobalPosY, GlobalPosZ,

		GlobalNorX, GlobalNorY, GlobalNorZ,

		LocPosX, LocPosY, LocPosZ,

		LocNorX, LocNorY, LocNorZ,

		U,V;

	

	

	TVector2 uv;

	

	if ( ymin < 0 ) ymin = 0;

	if ( ymax > fImageHeight) ymax = fImageHeight-1;

	

	//We fill and interpolate horizontally from left array to right array

	for (long y = ymin  ; y <= ymax ; y++ )

	{

		

		//Slope initialization for each item

		if ( (delta = leftX[y] - rightX[y]) == 0 )

		{

			deltaZ = 0;

			deltaGlobalPosX = 0; deltaGlobalPosY = 0; deltaGlobalPosZ = 0;

			deltaGlobalNorX = 0; deltaGlobalNorY = 0; deltaGlobalNorZ = 0;

			deltaU = 0; deltaV = 0;

			deltaLocPosX = 0; deltaLocPosY = 0; deltaLocPosZ = 0;

			deltaLocNorX = 0; deltaLocNorY = 0; deltaLocNorZ = 0;

		}

		

		else

		{

			deltaZ = (leftZ[y]-rightZ[y])/delta;

			deltaGlobalPosX = (leftGlobalPosX[y]-rightGlobalPosX[y])/delta;

			deltaGlobalPosY = (leftGlobalPosY[y]-rightGlobalPosY[y])/delta;

			deltaGlobalPosZ = (leftGlobalPosZ[y]-rightGlobalPosZ[y])/delta;

			

			deltaGlobalNorX = (leftGlobalNorX[y]-rightGlobalNorX[y])/delta;

			deltaGlobalNorY = (leftGlobalNorY[y]-rightGlobalNorY[y])/delta;

			deltaGlobalNorZ = (leftGlobalNorZ[y]-rightGlobalNorZ[y])/delta;

			

			if ( fFlags.fNeedsUV == (uint)true )

			{

				deltaU = (leftU[y]-rightU[y])/delta;

				deltaV = (leftV[y]-rightV[y])/delta;

			}

			

			if ( fFlags.fNeedsPointLoc == (uint)true )

			{

				deltaLocPosX = (leftLocPosX[y]-rightLocPosX[y])/delta;

				deltaLocPosY = (leftLocPosY[y]-rightLocPosY[y])/delta;

				deltaLocPosZ = (leftLocPosZ[y]-rightLocPosZ[y])/delta;

			}

			

			if ( fFlags.fNeedsNormalLoc == (uint)true )

			{

				deltaLocNorX = (leftLocNorX[y]-rightLocNorX[y])/delta;

				deltaLocNorY = (leftLocNorY[y]-rightLocNorY[y])/delta;

				deltaLocNorZ = (leftLocNorZ[y]-rightLocNorZ[y])/delta;

			}

		}

		

		//First value initialization

		z = leftZ[y];

		

		GlobalPosX = leftGlobalPosX[y];

		GlobalPosY = leftGlobalPosY[y];

		GlobalPosZ = leftGlobalPosZ[y];

		

		GlobalNorX = leftGlobalNorX[y];

		GlobalNorY = leftGlobalNorY[y];

		GlobalNorZ = leftGlobalNorZ[y];

		

		if ( fFlags.fNeedsPointLoc == (uint)true )

		{

			LocPosX = leftLocPosX[y];

			LocPosY = leftLocPosY[y];

			LocPosZ = leftLocPosZ[y];

		}

		if ( fFlags.fNeedsNormalLoc == (uint)true )

		{

			LocNorX = leftLocNorX[y];

			LocNorY = leftLocNorY[y];

			LocNorZ = leftLocNorZ[y];

		}

		if ( fFlags.fNeedsUV == (uint)true )

		{

			U = leftU[y];

			V = leftV[y];

		}

		

		//From left array to right array

		for ( int x = (int)(leftX[y]+0.5) ; x < (int)(rightX[y]+0.5) ; x++ )

		{	

			

			if ( x >= fFrontier && x < fImageWidth )

			{	//Clipping in x-axis

				

				int ind = y*fImageWidth + x;

				

				//Zbuffer test

				if ( z < fZbuffer[ind] )

				{

					fZbuffer[ind] = z;

					

					if ( fFlags.fNeedsPoint == (uint)true )

					{

						myShadingIn->fPoint[0] = GlobalPosX;

						myShadingIn->fPoint[1] = GlobalPosY;

						myShadingIn->fPoint[2] = GlobalPosZ;

					}

					

					if ( fFlags.fNeedsNormal == (uint)true )

					{

						myShadingIn->fGNormal[0] = GlobalNorX;

						myShadingIn->fGNormal[1] = GlobalNorY;

						myShadingIn->fGNormal[2] = GlobalNorZ;

					}

					

					if ( fFlags.fNeedsUV == (uint)true )

					{

						uv[0] = U;

						uv[1] = V;

						myShadingIn->fUV = uv;

					}

					

					if ( fFlags.fNeedsPointLoc == (uint)true )

					{

						myShadingIn->fPointLoc[0] = LocPosX;

						myShadingIn->fPointLoc[1] = LocPosY;

						myShadingIn->fPointLoc[2] = LocPosZ;

					}

					

					if ( fFlags.fNeedsNormalLoc == (uint)true )

					{

						myShadingIn->fNormalLoc[0] = LocNorX;

						myShadingIn->fNormalLoc[1] = LocNorY;

						myShadingIn->fNormalLoc[2] = LocNorZ;

					}

					//We fill ShadingIn structure and we get ShadingOut structure
					ShadingOut myShadingOut;
					CurrentInstance->DoShade(myShadingOut,*myShadingIn);

					TVertex3D thePoint;
					thePoint.fVertex[0] = GlobalPosX;
					thePoint.fVertex[1] = GlobalPosY;
					thePoint.fVertex[2] = GlobalPosZ;

					thePoint.fNormal[0] = GlobalNorX;
					thePoint.fNormal[1] = GlobalNorY;
					thePoint.fNormal[2] = GlobalNorZ;

					

					TVector2 pos;

					

					pos[0] = x - fOffscrOffset[0];

					pos[0] /= fZoom[0];

					pos[1] = -y + fOffscrOffset[1];

					pos[1] /= fZoom[1];

					

					TVector3 resultOrigin, resultDirection;

					

					//We get resultDirection giving the view direction for each pixel

					fCamera->CreateRay(&pos, &resultOrigin, &resultDirection);

					

					//We get color pixel

					TMCColorRGBA color;

					color.R = 10000;

					color.G = 00;

					color.B = 00;

					PixelColor(&myShadingOut, thePoint, fFlags.fChangesNormal,

						resultDirection, resultOrigin, color);

					

					//We display each pixel

					PixelSet(x,y,&color);

				}				

			}



			z += deltaZ;

			

			GlobalPosX += deltaGlobalPosX;

			GlobalPosY += deltaGlobalPosY;

			GlobalPosZ += deltaGlobalPosZ;

			

			GlobalNorX += deltaGlobalNorX;

			GlobalNorY += deltaGlobalNorY;

			GlobalNorZ += deltaGlobalNorZ;

			

			LocPosX += deltaLocPosX;

			LocPosY += deltaLocPosY;

			LocPosZ += deltaLocPosZ;

			

			LocNorX += deltaLocNorX;

			LocNorY += deltaLocNorY;

			LocNorZ += deltaLocNorZ;

			U += deltaU;

			V += deltaV;

		}	

	}

}





void MixRenderer::PixelColor(const ShadingOut *theShadingOut, const TVertex3D thePoint, const boolean changedNormals,

							  const TVector3 eyeDirection, const TVector3 eyePosition, TMCColorRGBA& resultColor)

{

	TMCColorRGBA tempColor;

	TVector3 newNormal;

	

	resultColor.R = theShadingOut->fColor.R * fRendEnv.fAmbient.R * theShadingOut->fAmbient + theShadingOut->fGlow.R;

	resultColor.G = theShadingOut->fColor.G * fRendEnv.fAmbient.G * theShadingOut->fAmbient + theShadingOut->fGlow.G;

	resultColor.B = theShadingOut->fColor.B * fRendEnv.fAmbient.B * theShadingOut->fAmbient + theShadingOut->fGlow.B;

	

	tempColor.R = theShadingOut->fColor.R * theShadingOut->fLambert;

	tempColor.G = theShadingOut->fColor.G * theShadingOut->fLambert;

	tempColor.B = theShadingOut->fColor.B * theShadingOut->fLambert;

	

	if (changedNormals)

	{

		newNormal[0] = theShadingOut->fChangedNormal[0];

		newNormal[1] = theShadingOut->fChangedNormal[1];

		newNormal[2] = theShadingOut->fChangedNormal[2];

	}

	else

	{

		newNormal[0] = thePoint.fNormal[0];

		newNormal[1] = thePoint.fNormal[1];

		newNormal[2] = thePoint.fNormal[2];

	}

	

	TVector3	reflectedLightDir;

	real		specAdjust = 48.0;	//48 gives a good reflective value

	real		specularCoeff;

	

	for (int index=0; index < fNbLights; index++)

	{

		TVector3		lightDirection;

		real			distanceToLight=10.0;

		real			lightNormalDP;

		TMCColorRGBA	lightColor;

		real			ShadowIntensity;

		int truc;

		



		truc=fLights[index]->GetDirection(thePoint.fVertex, lightDirection, distanceToLight);

		if (!truc)

			continue;



		truc=fLights[index]->GetColor(thePoint.fVertex, lightDirection, distanceToLight, lightColor, ShadowIntensity);

		if (!truc)

			continue;



		lightNormalDP = lightDirection*newNormal;


		//We must care for a possible atmospheric shader. The color of a lightsource may be modified when
		//getting through the atmosphere

		if (fRendEnv.fAtmosphere)
		{
			Ray3D ray;

			ray.fOrigin = thePoint.fVertex;

			ray.fDirection = lightDirection;

			TVector3	lightPos;

			real ratio = distanceToLight / lightDirection.GetMagnitude();

			fRendEnv.fAtmosphere->SegmentFilter2(ray, 0, ratio, lightColor, this,true,false,false,false);
		}


		//Diffuse color

		resultColor.R += tempColor.R * lightNormalDP * lightColor.R;
		resultColor.G += tempColor.G * lightNormalDP * lightColor.G;
		resultColor.B += tempColor.B * lightNormalDP * lightColor.B;

		

		//This is one method to calculate the reflected light direction

		lightNormalDP *= 2.0;

		reflectedLightDir[0] = -lightDirection[0] + lightNormalDP*newNormal[0];

		reflectedLightDir[1] = -lightDirection[1] + lightNormalDP*newNormal[1];

		reflectedLightDir[2] = -lightDirection[2] + lightNormalDP*newNormal[2];

		

		//Specular color

		real eyeReflectedLightDP = -eyeDirection[0] * reflectedLightDir[0] - eyeDirection[1] * reflectedLightDir[1] - eyeDirection[2] * reflectedLightDir[2];

		

		if (eyeReflectedLightDP > 0.0)

		{

			specularCoeff = (real)pow(eyeReflectedLightDP,theShadingOut->fSpecularSize*specAdjust);

			

			if (specularCoeff <= 0.0)

				continue;

			if (specularCoeff > 1.0)

				specularCoeff = 1.0;

			

			specularCoeff *= 2;		//Better specular highlights

			

			resultColor.R += theShadingOut->fSpecularColor.R * lightColor.R * specularCoeff;

			resultColor.G += theShadingOut->fSpecularColor.G * lightColor.G * specularCoeff;

			resultColor.B += theShadingOut->fSpecularColor.B * lightColor.B * specularCoeff;

			

		}

	}

	

	if (resultColor.R > 1.0)

		resultColor.R = 1.0;

	else if (resultColor.R < 0.0)

		resultColor.R = 0.0;	

	if (resultColor.G > 1.0)

		resultColor.G = 1.0;

	else if (resultColor.G < 0.0)

		resultColor.G = 0.0;	

	if (resultColor.B > 1.0)

		resultColor.B = 1.0;

	else if (resultColor.B < 0.0)

		resultColor.B = 0.0;	

	if (fRendEnv.fAtmosphere)
	{
		Ray3D ray;

		ray.fOrigin = eyePosition;

		ray.fDirection = thePoint.fVertex - eyePosition;

		fRendEnv.fAtmosphere->SegmentFilter2(ray,0,1,resultColor,this,false,true,false,false);
	}
}



//Used to fill pixels of infinite depth in Zbuffer

boolean MixRenderer::BackgroundColor(const TVector2& theScreenUV, TMCColorRGBA& resultColor)

{

	TVector2	UVPoint;

	Ray3D ray;



	resultColor.R = (real)0.2f;

	resultColor.G = (real)0.3f;

	resultColor.B = (real)0.4f;



	UVPoint[0] = theScreenUV[0]-fOffscrOffset[0];

	UVPoint[0] /= fZoom[0];

	UVPoint[1] = -theScreenUV[1]+fOffscrOffset[1];

	UVPoint[1] /= fZoom[1];



	fCamera->CreateRay(&UVPoint, &ray.fOrigin, &ray.fDirection);



	boolean fullAreaDone=false;

	//If there is no backdrop but a background, it appears behind the scene

	if (fRendEnv.fBackground)

	{

		fRendEnv.fBackground->GetEnvironmentColor(ray.fDirection,fullAreaDone,resultColor);

	}



	//Overrides the background

	if (fRendEnv.fBackdrop)
	{
		fRendEnv.fBackdrop->GetBackdropColor(UVPoint, fullAreaDone,fRendEnv.fuvMinMax, resultColor);
	}

	if (fRendEnv.fAtmosphere)
	{
		fRendEnv.fAtmosphere->DirectionFilter2(ray,resultColor,this, true, false, true);
	}

	return fullAreaDone;
}

boolean MixRenderer::GetRayColor	(
											TMCColorRGBA&		resColor,				// result color
									  const	RayHitParameters&	rayHitParams,			// parameter of the raytracing
									  const	RTLevelInfo&		level,					// level of reflection/transparency
									  const	TVector2&			screenCoordinates,		// coordinates of the pixel being rendered ( for backdrops)
											uint32&				numberOfRays,
											RayColorFlags		flags

											)
{
	RayHit3D* hit = rayHitParams.hit;
	
	ShadingOut myShadingOut;
	
	hit->fInstance->DoShade(myShadingOut,*hit);
	
	TVertex3D thePoint;
	
	thePoint.fVertex = hit->fPoint;
	
	thePoint.fNormal = hit->fGNormal;
	
	thePoint.fUV = hit->fUV;

	TVector3 resultOrigin, resultDirection;

	fCamera->CreateRay(&screenCoordinates, &resultOrigin, &resultDirection);

	hit->fInstance->GetShadingFlags(fFlags);

	numberOfRays = 1;

	PixelColor(&myShadingOut, thePoint, fFlags.fChangesNormal, resultOrigin, resultDirection, resColor );

	return true;
}

boolean	MixRenderer::GetLightIntensity(DirectLighting&			lighting,
									   int32					lightIndex,
									   const LightingContext&	lightingContext,
									   boolean					withShadow,
									   boolean					lightFromBehind
									   )
{
	lighting.fIsInShadow = false;

	LightingInfo* lightInfo = NULL;
	
	if (lightingContext.fFirstHitInfo && withShadow == fIllumSettings.fShadowsOn)	// we only cache the lighting that correspond to the illumination settings
	{
		lightInfo = &lightingContext.fFirstHitInfo->fLightInfo[lightIndex];

		if (lightInfo->fInitialised)
		{
			// lighting is already calculated

			lighting.fLightDirection	= lightInfo->fDirection;

			lighting.fLightColor		= lightInfo->fColor;

			return true;
		}
	}

	I3DShLightsource *light=fLights[lightIndex];

	boolean shouldLightInstance = light->ShouldLightInstance(lightingContext.fHit->fInstance);

	ShadowIO shadowIO;

	real incidentAngleCos=0;

	const RayHit3D& hit = *lightingContext.fHit;

	if (shouldLightInstance)
	{
		light->GetDirection(hit.fPoint,lighting.fLightDirection,shadowIO.fDistToLight);

		incidentAngleCos = lightingContext.fNormal * lighting.fLightDirection;
	}

	if (!shouldLightInstance || 
		(incidentAngleCos <= 0.0f && !lightFromBehind) || 
		!light->GetColor(hit.fPoint,lighting.fLightDirection,shadowIO.fDistToLight,lighting.fLightColor,shadowIO.fShadowIntensity))
	{
		lighting.fIsInShadow = true;

		if (lightInfo)
		{
			lightInfo->fInitialised	=	true;

			lightInfo->fColor.Set(0,0,0);

			lightInfo->fDirection	=	lighting.fLightDirection;
		}
		return false;
	}

	if (lightInfo)
	{
		lightInfo->fInitialised	=	true;

		lightInfo->fColor		=	lighting.fLightColor;

		lightInfo->fDirection	=	lighting.fLightDirection;
	}
	return true;

}
