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



	WireRenderer.cpp

  

	Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

	

	Author:	Jerome Bignon

	  

	Date:	19/07/99

	

	Revision History

		  

	when		who			what & why

	=========== ===========	=============================================================

	08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods

	19/07/99	Jerome		Creation

			

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



//#include "malloc.h"

#include "math.h"

#include "WireRenderer.h"

#include "MCCountedPtrHelper.h"

#include "I3DShUtilities.h"

#include "ToolBox.h"

#include "PublicUtilities.h"

#include "IShRasterLayerUtilities.h"
#include "I3DShRenderable.h"


const MCGUID CLSID_WireRenderer(R_CLSID_WireRenderer);





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_WireRenderer) res = new WireRenderer;

	

	return res;

}



// Constructor of the renderer

WireRenderer::WireRenderer()

{

	fAmbientColor.R = 0.5f;	

	fAmbientColor.G = 0.5f;

	fAmbientColor.B = 0.5f;

	fAtmosphere = NULL;

	fBackdrop = NULL;

	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;

	//	fOffscreen = NULL;

	fOffscrOffset[0] = 0.0f;

	fOffscrOffset[1] = 0.0f;

	fTreeTop = NULL;

	fZoom[0] = 1.0f;

	fZoom[1] = 1.0f;

}





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

//				BasicDataExchanger Methods

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



void* WireRenderer::GetExtensionDataBuffer()

{

	return (void*)NULL;

}	// used by the shell to set the new parameters



int16 WireRenderer::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 WireRenderer::SetTreeTop(I3DShGroup* treeTop)

{

	fTreeTop = treeTop;

	return MC_S_OK;

}



// specifies the rendering camera

MCCOMErr WireRenderer::SetCamera (I3DShCamera* camera)

{

	fCamera = camera;

	return MC_S_OK;

} 



// gives the background, backdrop & atmospheric shader.

MCCOMErr WireRenderer::SetEnvironment (I3DShEnvironment* environment)

{

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

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

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

	return MC_S_OK;

}	



// sets the ambient light color

MCCOMErr WireRenderer::SetAmbientLight (const TMCColorRGB &ambientColor)

{

	fAmbientColor = ambientColor;

	return MC_S_OK;

}



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

MCCOMErr WireRenderer::Dehydrate (int16 level)
{
	return MC_S_OK;
}



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

uint32 WireRenderer::GetRenderingTime ()

{

	return 0;

}



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

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

{

	return MC_S_OK;

}





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

//				WireRenderer Methods

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



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

void WireRenderer::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 ;				// Facet Mesh data
	TMCColorRGBA				color;

	fBuffer.red		= new uint8[fImageHeight*fImageWidth];
	fBuffer.green	= new uint8[fImageHeight*fImageWidth];
	fBuffer.blue	= new uint8[fImageHeight*fImageWidth];

	// clean the buffer

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

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

												//actually in Screen Coord 
	FacetMeshFacetIterator facetIterator;

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

	theKind = 0;

	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;

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

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


		TFacet3D facet;

		for ( facetIterator.First(); facetIterator.More(); facetIterator.Next())
		{ // browsing into facets list
			facet = facetIterator.GetFacet();
			Rasterize( facet, L2C, current->fInstance );			
		} 
	}	
}





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

MCCOMErr WireRenderer::PrepareDraw (const TMCPoint& size,const TBBox2D& uvBox,const TBBox2D& productionFrame, boolean (*callback) (int16 vv, void *priv),void* priv)

{
	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]));

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

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

	fTreeTop->QueryInterface(IID_I3DShTreeElement,(void**)&fTree);
	ThrowIfNil(fTree);

	fTree->BeginGetRenderables(fInstances);
	RenderScene(&fImageArea);

	return MC_S_OK;
}

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

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

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

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

void WireRenderer::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 WireRenderer::FinishDraw ()

{

	fTree->EndGetRenderables();

	fTree=NULL;

	fInstances=NULL;


	delete []fBuffer.red;
	delete []fBuffer.green;
	delete []fBuffer.blue;

	return MC_S_OK;

}	



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

void WireRenderer::BlocCopy(const RTData* pixels)
{
	int 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; //pixels->columnBytes>>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)
			{
				alpha[i*columnStep] = 255;
			}
		}
		red		+=toNextLine;
		green	+=toNextLine;
		blue	+=toNextLine;
		
		if (alpha)
		{
			alpha   +=toNextLine;
		}
	}
}


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

void WireRenderer::Rasterize(TFacet3D facet, TTransform3D L2C, I3DShInstance* CurrentInstance )
{	
	//We transform Vertex in Local to Vertex in Camera Coord using L2C
	TVector3 vertex1, vertex2, vertex3;
	
	WireTransformPoint(vertex1, L2C, facet.fVertices[0].fVertex); // This is a call from the toolbox we
	WireTransformPoint(vertex2, L2C, facet.fVertices[1].fVertex); // have created
	WireTransformPoint(vertex3, L2C, facet.fVertices[2].fVertex);
	
	TMCColorRGBA	color, notused;
	
	CurrentInstance->GetMainColors(color, notused);
		
	TVector2 	screenVertex1;
	TVector2	screenVertex2;
	TVector2	screenVertex3;

	real		zOut ;	

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

	boolean result1 = fCamera->Project3DTo2D(&vertex1, &screenVertex1, &zOut);
	boolean result2 = fCamera->Project3DTo2D(&vertex2, &screenVertex2, &zOut);
	boolean result3 = fCamera->Project3DTo2D(&vertex3, &screenVertex3, &zOut);

	

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

	screenVertex1[0] = fOffscrOffset[0] + (screenVertex1[0] * fZoom[0]) ;

	screenVertex1[1] = fOffscrOffset[1] - (screenVertex1[1] * fZoom[1]) ;

	

	screenVertex2[0] = fOffscrOffset[0] + (screenVertex2[0] * fZoom[0]) ;

	screenVertex2[1] = fOffscrOffset[1] - (screenVertex2[1] * fZoom[1]) ;

	

	screenVertex3[0] = fOffscrOffset[0] + (screenVertex3[0] * fZoom[0]) ;

	screenVertex3[1] = fOffscrOffset[1] - (screenVertex3[1] * fZoom[1]) ;

	

	DrawLine((int16)screenVertex1[0], (int16)screenVertex1[1], (int16)screenVertex2[0], (int16)screenVertex2[1], &color ) ;

	DrawLine((int16)screenVertex2[0], (int16)screenVertex2[1], (int16)screenVertex3[0], (int16)screenVertex3[1], &color ) ;

	DrawLine((int16)screenVertex3[0], (int16)screenVertex3[1], (int16)screenVertex1[0], (int16)screenVertex1[1], &color ) ;

	

}



void WireRenderer::GetRenderStatistics(I3DRenderStatistics** renderstats)

{

	TMCCountedGetHelper<I3DRenderStatistics> result(renderstats);

	result = static_cast<I3DRenderStatistics*>(NULL);

}



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

void WireRenderer::PixelSet (int32 x, int32 y, TMCColorRGBA* color)
{
	if ((x>=0)&&(x<fImageWidth)&&(y>=0)&&(y<fImageHeight))
	{
		fBuffer.red[x+fImageWidth*y] = color->R;
		fBuffer.green[x+fImageWidth*y] = color->G;
		fBuffer.blue[x+fImageWidth*y] = color->B;
	}
}



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

void WireRenderer::DrawLine(int16 x1, int16 y1, int16 x2, int16 y2, TMCColorRGBA* Color )
{
	int16	x,y,xi,yi,i,c,dx,dy;

	x=x1;
	y=y1;


	PixelSet(x, y, Color);

	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)

	{

		c=dy >> 1;

		

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

		{

			y=y+yi;

			c=c+dx;

			

			if (c >= dy)

			{

				c=c-dy;

				x=x+xi;

			}

			PixelSet(x, y, Color);

		}

	}

	

	else

	{

		c=dx >> 1;

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

		{

			x=x+xi;

			c=c+dy;

			if (c >= dx)

			{

				c=c-dx;

				y=y+yi;

			}

			PixelSet(x, y, Color);

		}

    }

}







	









