///////////////////////////////////////////////////////////////////////////////
/// @file RenderCode.cpp
///
/// @brief Methods for doing actual rendering
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Oct 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deRender_priv.hpp"
#include "deScene.hpp"
#include "deCamera.hpp"
#include "dePortal.hpp"
#include "deProfile.hpp"
#include "deBitmap.hpp"
#include "deRenderStates.hpp"
#include "de2D.hpp"
#include "deHash.hpp"
#include "deSurface.hpp"

struct TransparentRenderObject
{
	IdeRender::deRenderObject * RenderObject;
	deDouble Depth;
};

deBoolean deRender::BeginFrame(HWND WindowHandle, deBoolean ClearColorBuffer)
{
	PROFILE_ID_NAME(&RenderProfileID, "Render");
	IdeDriver*	pDriver;

	if (IsInFrame(WindowHandle, deTRUE))
	{
		DEBUGRETURN(deFALSE,"BeginFrame: already in frame - call EndFrame to end the previous one");
	}
	SetInFrame(WindowHandle, deTRUE);
	
	m_FrameNum++;
	// wrap it to positive-only
	m_FrameNum &= 0x7FFFFFFF;

	pDriver = GetDriver(WindowHandle);
	if (!pDriver->BeginFrame())
	{
		//CleanUp();
		SetInFrame(WindowHandle, deFALSE);
		DEBUGRETURN(deFALSE,"BeginFrame: IdeDriver::BeginFrame() failed");
	}

	// setup the bookkeeping stuff
	if (m_RenderStats == NULL)
		m_RenderStats = &s_RenderStats;
	memset(m_RenderStats, 0, sizeof(deRenderStats));
	
	deRect Rect = pDriver->GetDisplayRect();
	// set the viewport
	if (!pDriver->SetViewport(0, 0, Rect.Right - Rect.Left, Rect.Bottom-Rect.Top, 0, 1))
	{
		DEBUGRETURN(deFALSE,"BeginFrame: IdeDriver::SetViewport() failed");
	}

	if (ClearColorBuffer)
	{
		if(!pDriver->ClearFrame(m_BGColor, DE_TRUE, DE_TRUE, DE_TRUE))
		{
			DEBUGRETURN(deFALSE,"BeginFrame: IdeDriver::ClearFrame() failed, clearing color buffer");
		}
	}
	else
	{
		if(!pDriver->ClearFrame(m_BGColor, DE_FALSE, DE_TRUE, DE_TRUE))
		{
			DEBUGRETURN(deFALSE,"BeginFrame: IdeDriver::ClearFrame() failed, not clearing color buffer");
		}
	}

	return deTRUE;
}

#if defined(DESTINY3D_FREEWARE) || defined(DESTINY3D_WATERMARK)
#include "deFile.hpp"

extern size_t sizeofde3dlogo;
extern unsigned char de3dlogo[];
deBoolean deRender::RenderWaterMark(IdeDriver* pDriver)
{
#if !defined(DESTINY3D_FREEWARE)
	if (m_EnableWaterMark == 0)
		return deTRUE;
#endif
	static double TotalTime = 0.0;
	static deTimer_QPC timer; // reliable timer service
	if (TotalTime == 0.0)
		timer.GrabStartTime();
	timer.GrabStopTime();
	double delta = timer.DiffTime(1.0);
	TotalTime += delta;
	timer.GrabStartTime();
	static int logoWidth=0, logoHeight=0;
	if (!m_WaterMarkTex)
	{
		IdeFileMemory* memfile = IdeFile_CreateFileMemory();
		if (!memfile->Open(de3dlogo, sizeofde3dlogo))
			DEBUGRETURN(deFALSE, "Could not load watermark a!");
		IdeResourceBase* pRsc = IdeResourceManager_GetManager()->GetResourceFromFile(memfile, "destiny3dlogo_2.dds", deFALSE);
		IdeBitmapProxy* proxy = IdeBitmap_CreateProxy(pRsc);
		if (!proxy)
		{
			if (pRsc)
				pRsc->Release();
			DEBUGRETURN(deFALSE, "Could not load watermark b!");
		}
		logoWidth = proxy->GetWidth() / 2;
		logoHeight = proxy->GetHeight() / 2;
		m_WaterMarkTex = IdeRenderState_CreateRenderTexture();
		if (!m_WaterMarkTex)
		{
			if (proxy)
				proxy->Release();
			if (pRsc)
				pRsc->Release();
			DEBUGRETURN(deFALSE, "Could not load watermark c!");
		}
		m_WaterMarkTex->Lock();
		m_WaterMarkTex->SetBitmap(0, proxy);
		m_WaterMarkTex->SetColorArg(0, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		m_WaterMarkTex->SetColorOperation(0, IdeRenderTexture::TEXTUREOP_SELECTARG1);
		m_WaterMarkTex->SetAlphaArg(0, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		m_WaterMarkTex->SetAlphaArg(0, 2, IdeRenderTexture::TEXTUREARG_DIFFUSE);
		m_WaterMarkTex->SetAlphaOperation(0, IdeRenderTexture::TEXTUREOP_MODULATE);
		m_WaterMarkTex->Unlock();
		proxy->Release();
		pRsc->Release();
		memfile->Release();
	}
	{
		float FadeOut = 5;
		float FadeIn = 20;
		float FadeLength = 5;
		float AnimLength = FadeIn + FadeLength;
		float AnimCycle = (float)fmod(TotalTime, AnimLength);
		float Opacity;
		if (AnimCycle < FadeOut)
			Opacity = 1.0;
		else if (AnimCycle > FadeOut+FadeLength && AnimCycle < FadeIn)
			Opacity = 0.0;
		else if (AnimCycle < FadeOut+FadeLength)
			Opacity = 1.0f - (AnimCycle - FadeOut) / FadeLength;
		else if (AnimCycle < FadeIn+FadeLength)
			Opacity = (AnimCycle - FadeIn) / FadeLength;
		else
			Opacity = 1.0;
		u32 color = 0x00FFFFFF;
		color |= ((u32)(Opacity * 255)) << 24;
#if !defined(DESTINY3D_FREEWARE)
		if (m_EnableWaterMark != 2)
			color = 0x80FFFFFF;
#endif
		
		deRect ClientRect = pDriver->GetDisplayRect();
		deRect LogoRect;
		LogoRect.Top = ClientRect.Top + 10;
		LogoRect.Right = ClientRect.Right - 10;
		LogoRect.Bottom = LogoRect.Top + logoHeight;
		LogoRect.Left = LogoRect.Right - logoWidth;
		
		{
			deVertex *			Positions;
			deARGB *			Colors;
			deTexCoord *		Coords;
			const int			PointCount = 4;

			if (!m_WaterMarkVB)
			{
				m_WaterMarkVB = IdeVertexBuffer_CreateVBuffer();
				if(!m_WaterMarkVB)
					DEBUGRETURN(deFALSE, "Could not load watermark e!");
			}

			//lock it
			m_WaterMarkVB->Lock();

			if(!m_WaterMarkVB->SetBufferSize(PointCount, deFALSE))
				DEBUGRETURN(deFALSE, "Could not load watermark f!");

			m_WaterMarkVB->SetDynamicStatus(deTRUE, deFALSE);

			//allocate memory for the points
			m_WaterMarkVB->CreateBuffer(IdeVertexBuffer::BUFFER_POSITION, deFALSE);

			//a buffer for the color
			m_WaterMarkVB->CreateBuffer(IdeVertexBuffer::BUFFER_DIFFUSE, deFALSE);

			//add in the UV coords for the texture
			m_WaterMarkVB->CreateBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0, deFALSE);

			//grab the buffers now that we created and fill them
			Positions = (deVertex *)m_WaterMarkVB->GetBuffer(IdeVertexBuffer::BUFFER_POSITION);
			Colors = (deARGB *)m_WaterMarkVB->GetBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
			Coords = (deTexCoord *)m_WaterMarkVB->GetBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);

			if (!Positions || !Coords || !Colors)
			{
				//something failed, destroy and exit
				m_WaterMarkVB->Release();
				DEBUGRETURN(deFALSE, "Could not load watermark g!");
			}

			Positions[0].x = (deFloat)LogoRect.Left;
			Positions[0].y = (deFloat)LogoRect.Top;
			Positions[0].z = 1.0f;
			Positions[1].x = (deFloat)LogoRect.Left;
			Positions[1].y = (deFloat)LogoRect.Bottom;
			Positions[1].z = 1.0f;
			Positions[2].x = (deFloat)LogoRect.Right;
			Positions[2].y = (deFloat)LogoRect.Top;
			Positions[2].z = 1.0f;
			Positions[3].x = (deFloat)LogoRect.Right;
			Positions[3].y = (deFloat)LogoRect.Bottom;
			Positions[3].z = 1.0f;

			Coords[0].u = 0;
			Coords[0].v = 0;
			Coords[1].u = 0;
			Coords[1].v = 1;
			Coords[2].u = 1;
			Coords[2].v = 0;
			Coords[3].u = 1;
			Coords[3].v = 1;

			Colors[0] = Colors[1] = Colors[2] = Colors[3] = color;

			//make sure we are doing a tri list
			m_WaterMarkVB->SetRenderType(IdeVertexBuffer::RENDER_TRIANGLESTRIP);

			//make sure the buffers are dirtied
			m_WaterMarkVB->DirtyBuffer(IdeVertexBuffer::BUFFER_POSITION);
			m_WaterMarkVB->DirtyBuffer(IdeVertexBuffer::BUFFER_DIFFUSE);
			m_WaterMarkVB->DirtyBuffer(IdeVertexBuffer::BUFFER_TEXTURECOORDS0);

			m_WaterMarkVB->Unlock();
			pDriver->UpdateVertexBuffer(m_WaterMarkVB);
		}
		{
			int Width, Height;
			Width = ClientRect.Right - ClientRect.Left;
			Height = ClientRect.Bottom - ClientRect.Top;
			
			// set the "clipping rect"
			if(!pDriver->SetViewport(0, 0, Width, Height, 0.0f, 1.0f))
			{
				DEBUGRETURN(deFALSE, "Could not render watermark h!");
			}
			
			//set the driver up for a 2d render
			deRect SubRect = {0,0, 0,0};
			if(!pDriver->SetOrthoClippingSubRect((deFloat)Width, (deFloat)Height, 0.1f, 1000.0f, deTRUE, SubRect))
			{
				DEBUGRETURN(deFALSE, "Could not render watermark i!");
			}
		}
		// set up rendering context
		u32 ZEnable = pDriver->GetRenderState(IdeDriver::RENDER_ZENABLE);
		u32 FaceCull = pDriver->GetRenderState(IdeDriver::RENDER_CULLMODE);
		u32 FillMode = pDriver->GetRenderState(IdeDriver::RENDER_FILLMODE);
		pDriver->SetRenderState(IdeDriver::RENDER_ZENABLE, deFALSE);
		pDriver->SetRenderState(IdeDriver::RENDER_CULLMODE, IdeDriver::CULL_NONE);
		pDriver->SetRenderState(IdeDriver::RENDER_FILLMODE, IdeDriver::FILL_SOLID);
		pDriver->SetRenderState(IdeDriver::RENDER_ALPHABLENDENABLE, deTRUE);
		pDriver->SetRenderState(IdeDriver::RENDER_SOURCEBLEND, IdeDriver::BLEND_SOURCEALPHA);
		pDriver->SetRenderState(IdeDriver::RENDER_DESTBLEND, IdeDriver::BLEND_INVERSESOURCEALPHA);
		pDriver->SetRenderState(IdeDriver::RENDER_ALPHATESTENABLE, deFALSE);
		pDriver->SetRenderState(IdeDriver::RENDER_HWLIGHTINGENABLE, deFALSE);
		pDriver->SetRenderState(IdeDriver::RENDER_FSAAENABLE, deFALSE);
		pDriver->SetRenderState(IdeDriver::RENDER_FOGENABLE, 0);

		deTransform position;
		pDriver->SetWorldTransform(&position);
		pDriver->SetCameraTransform(&position);
		
		pDriver->SetRenderState(m_WaterMarkTex);

		// dispatch render
		pDriver->Render(m_WaterMarkVB, 0, NULL);

		// restore some render context
		pDriver->SetRenderState(IdeDriver::RENDER_ZENABLE, ZEnable);
		pDriver->SetRenderState(IdeDriver::RENDER_CULLMODE, FaceCull);
		pDriver->SetRenderState(IdeDriver::RENDER_FILLMODE, FillMode);
	}
	return deTRUE;
}
#else
deBoolean deRender::RenderWaterMark(IdeDriver*)
{
	return deTRUE;
}
#endif // defined(DESTINY3D_FREEWARE) || defined(DESTINY3D_WATERMARK)

deBoolean deRender::EndFrame(HWND WindowHandle, deDouble DeltaTime)
{
	PROFILE_ID_NAME(&RenderProfileID, "Render");
	IdeDriver*	pDriver;
	Ide2DCollection * BlitCollection;

	if (!IsInFrame(WindowHandle, deFALSE))
	{
		DEBUGRETURN(deFALSE,"EndFrame: not in a frame - call BeginFrame to start one first");
	}

	pDriver = GetDriver(WindowHandle);

	deTransform M;
	pDriver->SetWorldTransform(&M);

	if (!pDriver->SetRenderState(s_BlankMat))
	{
		DEBUGRETURN(deFALSE,"EndFrame: failed to set blank material");
	}
	
	BlitCollection = Get2DCollection(WindowHandle);
	if (BlitCollection)
	{
		if (!BlitCollection->Render(pDriver, DeltaTime, s_BlankTex, m_RenderStats))
		{
			DEBUGRETURN(deFALSE,"EndFrame: Ide2DCollection::Render() failed");
		}
	}

	if (!RenderWaterMark(pDriver))
		return deFALSE;
	
	if(!pDriver->EndFrame())
	{
		DEBUGRETURN(deFALSE,"EndFrame: IdeDriver::EndFrame() failed");
	}

	SetInFrame(WindowHandle, deFALSE);

	{
		IdeDriver::deDriverStats *stats;
		stats = pDriver->GetDriverStats();
		if (stats)
			m_RenderStats->DriverStats = *stats;
		pDriver->ResetDriverStats();
		m_RenderStats = NULL;
	}
	
	return deTRUE;
}

deBoolean deRender::Render(IdeCamera * cam, deDouble DeltaTime, deBoolean UseSG, deBoolean ClearColorBuffer, deBoolean BeginFrame, deBoolean EndFrame)
{
	deBoolean RetVal;
	IdeDriver*	pDriver;
	IdeSceneGraph * pSG;
	if (UseSG)
		pSG = IdeSceneGraph_GetSceneGraphPtr();
	else
		pSG = NULL;

	pDriver = SetupRenderTarget(NULL, cam);

	if (!pDriver)
	{
		DEBUGRETURN(deFALSE,"Render: specified parameter was NULL: 'pDriver'");
	}

	RetVal = RenderGeometry(pDriver, pSG, cam, DeltaTime, ClearColorBuffer, BeginFrame, EndFrame);
	if (!RetVal)
	{
		DEBUGMSG0("Render: RenderGeometry() failed");
	}

	return RetVal;
}

deBoolean deRender::RenderTexture(IdeBitmap * Target, IdeCamera * cam, deDouble DeltaTime)
{
	IdeDriver*	pDriver;
	IdeCamera * tempCam = IdeCamera_CreateCameraFromRef(cam);
	IdeSceneGraph * pSG = IdeSceneGraph_GetSceneGraphPtr();
	
	pDriver = SetupRenderTarget(Target, tempCam);

	if (!pDriver)
	{
		DEBUGRETURN(deFALSE,"RenderTexture: specified parameter was NULL: 'pDriver'");
	}

	deBoolean RetVal;
	
	RetVal = RenderGeometry(pDriver, pSG, tempCam, DeltaTime, deTRUE, deFALSE, deFALSE);
	if (!RetVal)
	{
		DEBUGMSG0("RenderTexture: RenderGeometry() failed");
	}

	tempCam->Release();

	return RetVal;
}

//private
IdeDriver* deRender::SetupRenderTarget(IdeBitmap * Target, IdeCamera * cam)
{
	PROFILE_ID_NAME(&RenderProfileID, "Render");
	deRect		Viewport;
	HWND		hWnd;
	IdeDriver*	pDriver;
	int Width, Height;
	int Top, Left;
	deDouble Aspect;
	deBoolean RenderTarget;

	if (!cam)
	{
		DEBUGRETURN(NULL,"SetupRenderTarget: specified parameter was NULL: 'cam'");
	}

	if (Target)
	{
		if (Target->IsRenderTarget())
			RenderTarget = deTRUE;
		else
		{
			DEBUGRETURN(NULL,"SetupRenderTarget: attempted to use a non-rendertarget bitmap");
		}
	}
	else
		RenderTarget = deFALSE;
	
	// grab the camera attributes
	Viewport = cam->GetRect();
	hWnd = cam->GetWindow();

	// get the driver for the current window
	pDriver = GetDriver(hWnd);

	if (!pDriver)
	{
		DEBUGRETURN(NULL,"SetupRenderTarget: could not retrieve a driver for camera's window");
	}

	Top = Viewport.Top;
	Left = Viewport.Left;
	Width = Viewport.Right - Viewport.Left;
	Height = Viewport.Bottom - Viewport.Top;
	
	Aspect = ((deDouble)Width)/Height;
	
	// get a backbuffer rect to render to
	if (RenderTarget)
	{
		deFloat Scale = (deFloat)Target->GetScalingFactor();
		Scale = 1.0f/Scale;
		Top = int(Top*Scale);
		Left = int(Left*Scale);
		Width = int(Width*Scale);
		Height = int(Height*Scale);

		// set the render-target to the texture, implicitly calls SetViewport also
		if (!pDriver->RenderTo(Target))
		{
			DEBUGRETURN(NULL,"SetupRenderTarget: IdeDriver::RenderTo(Target) failed");
		}
	}
	else
	{
		// set the render-target to the screen
		if (!pDriver->RenderTo(NULL))
		{
			DEBUGRETURN(NULL,"SetupRenderTarget: IdeDriver::RenderTo(NULL) failed");
		}
	}
	
	// set the viewport
	if (!pDriver->SetViewport(Left, Top, Width, Height, 0, 1))
	{
		DEBUGRETURN(NULL,"SetupRenderTarget: IdeDriver::SetViewport() failed");
	}

	return pDriver;
}

static deBoolean RectsIntersect(deRect & r1, deRect & r2)
{
	if (r1.Right < r2.Left)
		return deFALSE;
	if (r1.Left > r2.Right)
		return deFALSE;
	if (r1.Top < r2.Top)
		return deFALSE;
	if (r1.Bottom > r2.Bottom)
		return deFALSE;
	return deTRUE;
}

//private
deBoolean deRender::RenderGeometry(IdeDriver * pDriver, IdeSceneGraph * SG, IdeCamera * cam, deDouble DeltaTime, deBoolean ClearColorBuffer, deBoolean BeginFrame, deBoolean EndFrame)
{
	PROFILE_ID_NAME(&RenderProfileID, "Render");
	deTArray<deTList <deRenderBatch*> > UsedBatches(m_TPortalDepth);
	deRenderPassInfo	RenderPassInfo;
	deRenderBatch		*pBatch;
	void		*ptr, *batchptr;
	int			BatchCount, PrevBatchLevel;
//	int			TriCount;
	deTransform	Position;
	deDouble	FarPlane;
	int			SceneCount=0;
	HWND		hWnd;

	m_CurrentTimeStep = DeltaTime;
	
	// grab some camera stuff
	hWnd = cam->GetWindow();
	Position = cam->Position();

	if (m_RenderStats == NULL)
		m_RenderStats = &s_RenderStats;

	if (!SG)
	{
		DEBUGMSG0("RenderGeometry: no scenegraph available");
		goto Render_EndFrame;
	}
	else
	{
		// set up the traversal parameters
		IdeSceneGraph::deSceneTraversal TraversalData;
		TraversalData.cam = cam;
		TraversalData.render = this;
		TraversalData.MaxTPortalDepth = m_TPortalDepth;
		// stack up our current driver pointer
		m_CurrentPassDriver.Push(pDriver);
		// clear out some stuff
		m_RenderBatches.EmptyList();
		// insert our "root-level" batch
		deRect Rect = cam->GetRect();
		m_LightsEncountered = 0;
		RenderPrepBatch(0, NULL, Rect, NULL, NULL);
		// traverse the scenegraph
		//		SceneCount = SG->Traverse(cam, this, m_TPortalDepth);
		SceneCount = SG->Traverse(&TraversalData);
		m_CurrentPassDriver.Pop();
		FarPlane = cam->GetFarClip();
		FarPlane = min(FarPlane, TraversalData.FarthestDistanceFound);
		m_RenderStats->FarPlaneDistUsed = (deFloat)FarPlane;
	}
	
	// set the hardware FOV
	{
		deDouble Aspect;
		deRect CamRect;
		CamRect = cam->GetRect();
		Aspect = ((deDouble)(CamRect.Right - CamRect.Left))/(CamRect.Bottom - CamRect.Top);
		if (cam->IsOrtho())
		{
			deDouble OrthoWidth = cam->GetOrthoWidth();
			pDriver->SetOrthoClipping(float(OrthoWidth), float(OrthoWidth/Aspect), float(cam->GetNearClip()), float(FarPlane));
		}
		else
		{
			// special aspect to maintain biggest side dominance
			deDouble	FOV;
			FOV = cam->GetFOVy(Aspect);
			pDriver->SetFOVClipping(float(FOV), float(Aspect), float(cam->GetNearClip()), float(FarPlane));
		}
	}

	// begin if we are the first camera
	if (BeginFrame && !IsInFrame(hWnd, deTRUE))
	{
		// setup the bookkeeping stuff
		if (m_RenderStats == NULL)
			m_RenderStats = &s_RenderStats;
		memset(m_RenderStats, 0, sizeof(deRenderStats));
		
		if (!pDriver->BeginFrame())
		{
			DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::BeginFrame() failed");
		}
		SetInFrame(hWnd, deTRUE);
		if (!ClearColorBuffer)
		{
			if(!pDriver->ClearFrame(m_BGColor, deFALSE, deTRUE, deTRUE))
				DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::ClearFrame() failed - clearing color buffer");
		}
	}
	
	if (ClearColorBuffer)
	{
		if(!pDriver->ClearFrame(m_BGColor, deTRUE, deTRUE, deTRUE))
			DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::ClearFrame() failed - not clearing color buffer");
	}

	// funky D3D8 camera settings
	pDriver->SetCameraTransform(&Position);

	// let the driver know how we want to fill & cull
	if (!pDriver->SetRenderState(IdeDriver::RENDER_CULLMODE, m_CullState))
		DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_CULLMODE failed");
	if (!pDriver->SetRenderState(IdeDriver::RENDER_FILLMODE, m_FillState))
		DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FILLMODE failed");
	if (!pDriver->SetRenderState(IdeDriver::RENDER_ZENABLE, deTRUE))
		DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_ZENABLE : deTRUE failed");
	// no alpha blending for this pass, only objects marked Transparent can use it
	if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHABLENDENABLE, deFALSE))
		DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_ALPHABLENDENABLE : deFALSE failed");

	{
		deFloat FogNear, FogFar;
		deARGB FogColor;
		deBoolean RangeFog;
		DWORD FogType;
		if (cam->GetFog(FogNear, FogFar, FogColor, RangeFog))
		{
			FogType = RangeFog ? 2 : 1;
			if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGENABLE, FogType))
				DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGENABLE : deTRUE failed");
			if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGSTART, *((DWORD*)&FogNear)))
				DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGSTART failed");
			if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGEND, *((DWORD*)&FogFar)))
				DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGEND failed");
			if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGCOLOR, FogColor))
				DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGCOLOR failed");
		}
		else
		{
			if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGENABLE, deFALSE))
				DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGENABLE : deFALSE failed");
		}
	}

	BatchCount = 0;
	PrevBatchLevel = 0;
	batchptr = m_RenderBatches.GetNext(NULL, pBatch);
	while (batchptr)
	{
		long z_bias = 0;
		BatchCount++;

		RenderPassInfo.pDriver = pDriver;
		RenderPassInfo.pBatch = pBatch;
		RenderPassInfo.pCam = cam;
		RenderPassInfo.ColorWrite = deTRUE;
		RenderPassInfo.DepthWrite = deTRUE;

		if (pBatch->TPortal)
		{
			//SetSubViewport(pDriver, cam, pBatch->Viewport);
			SetScissorRect(pDriver, pBatch->Viewport);
		}
		else
		{
			// if we've already set a sub-viewport, need to restore full-screen for this portal
			if (BatchCount > 1)
			{
				SetupRenderTarget(NULL, cam);
			}
		}

		// set these up for either the z-only pass, or the regular geometry if no z-only being done
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ZWRITESENABLE, deTRUE))
			DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_ZWRITESENABLE : deTRUE failed");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_DEPTHFUNCTION, IdeDriver::COMPARE_LESSEQUAL))
			DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_DEPTHFUNCTION : COMPARE_LESSEQUAL failed");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHATESTENABLE, deFALSE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHATESTENABLE : deFALSE failed");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_AMBIENTLIGHT, 0x00000000))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_AMBIENTLIGHT : 0x00000000 failed");
		
		if (m_RenderSolidWireframe)
		{
			z_bias = pDriver->GetRenderState(IdeDriver::RENDER_DEPTHBIAS);
			pDriver->SetRenderState(IdeDriver::RENDER_DEPTHBIAS, 0);
			pDriver->SetRenderState(IdeDriver::RENDER_FILLMODE, IdeDriver::FILL_SOLID);
			//pDriver->SetRenderState(IdeDriver::RENDER_CULLMODE, IdeDriver::CULL_CCW);
			pDriver->SetRenderState(IdeDriver::RENDER_DEPTHFUNCTION, IdeDriver::COMPARE_LESS);

			RenderPassInfo.ColorWrite = deFALSE;
			RenderPassInfo.DepthWrite = deTRUE;
			if (!RenderBatch(&RenderPassInfo))
				DEBUGRETURN(deFALSE, "RenderGeometry: RenderBatch failed (Z-Only)");
			RenderPassInfo.ColorWrite = deTRUE;
			RenderPassInfo.DepthWrite = deFALSE;

			pDriver->SetRenderState(IdeDriver::RENDER_DEPTHBIAS, 1);
			pDriver->SetRenderState(IdeDriver::RENDER_FILLMODE, IdeDriver::FILL_WIREFRAME);
			//pDriver->SetRenderState(IdeDriver::RENDER_CULLMODE, IdeDriver::CULL_CW);
			pDriver->SetRenderState(IdeDriver::RENDER_DEPTHFUNCTION, IdeDriver::COMPARE_LESSEQUAL);
		}

		if (!RenderBatch(&RenderPassInfo))
				DEBUGRETURN(deFALSE, "RenderGeometry: RenderBatch failed");

		// if we are at a lower level than the previous run-through, we don't need to respect
		//   the portals that were rendered at a higher level
		if (PrevBatchLevel > pBatch->PortalLevel)
			UsedBatches[PrevBatchLevel].EmptyList();
		
		// only render a "glaze" if we have a T-Portal and aren't rendering the very last batch
		if (pBatch->TPortal && BatchCount < m_RenderBatches.Length())
		{
			IdeVertexBuffer * tempVB;
			deRenderBatch * tempBatch;
			int tempCounter;

			// wipe the depth buffer inside this viewport
			pDriver->ClearFrame(0, deFALSE, deTRUE, deTRUE);

			// disable color buffer
			if (!SetStencilState(pDriver, Stencil_UseColor, deFALSE))
				DEBUGRETURN(deFALSE,"RenderGeometry: SetStencilState(Stencil_UseColor, deFALSE) failed");

			if (!pDriver->SetRenderState(s_BlankTex))
				DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(s_BlankTex) failed - portal glazing");

			pDriver->SetWorldTransform(&(pBatch->Position));

			// render our "glaze"
			if (!pDriver->Render(pBatch->TPortal->GetVBuffer(), 0, NULL))
				DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::Render() failed - portal glaze");

			// restore the "glaze" for any portals that have intersecting screen-rects
			for (tempCounter = 0; tempCounter <= pBatch->PortalLevel; tempCounter++)
			{
				ptr = UsedBatches[tempCounter].GetNext(NULL, tempBatch);
				while (ptr)
				{
					tempVB = tempBatch->TPortal->GetVBuffer();
					if (tempVB && RectsIntersect(pBatch->Viewport, tempBatch->Viewport))
					{
						pDriver->SetWorldTransform(&(pBatch->Position));
						if (!pDriver->Render(tempVB, 0, NULL))
							DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::Render() failed - portal glaze restoration");
					}
					ptr = UsedBatches[tempCounter].GetNext(ptr, tempBatch);
				}
			}
			
			// enable color buffer
			if (!SetStencilState(pDriver, Stencil_UseColor, deTRUE))
				DEBUGRETURN(deFALSE,"RenderGeometry: SetStencilState(Stencil_UseColor, deTRUE) failed");
		}
		if (m_RenderSolidWireframe)
		{
			pDriver->SetRenderState(IdeDriver::RENDER_DEPTHBIAS, z_bias);
		}

		UsedBatches[pBatch->PortalLevel].AddElement(pBatch);

		pBatch->ObjectTree.EmptyBST();
		
		batchptr = m_RenderBatches.GetNext(batchptr, pBatch);
	}

	m_RenderBatches.EmptyList();
	
Render_EndFrame:
	// end if we are the last camera
	if (EndFrame)
	{
		if (!this->EndFrame(hWnd, DeltaTime))
		{
			DEBUGRETURN(deFALSE,"RenderGeometry: EndFrame() failed");
		}
	}
	return deTRUE;
}

// optimized for a 2-texture-unit, "8-texture-stage" video chip
deBoolean deRender::RenderBatch(deRenderPassInfo* pInfo)
{
	deTList<deRenderObject*>  SkyList;
	deTList <TransparentRenderObject>	Transparents;
	TransparentRenderObject * pTransObj, TransObj;
	IdeRenderMaterial	*Material, *PrevMaterial;
	IdeRenderTexture	*Texture, *PrevTexture;
	deRenderObject		*pObject;
	deRenderBatch		*pBatch;
	IdeDriver			*pDriver;
	IdeCamera			*pCam;
	void				*ptr, *ptr2;
	int			TriCount;
	int			counter;
	deVec3d		camfwd;
	deVec3d		campos;
	long		TempLong;
	bool		UsedBlankTex;
	
	pDriver = pInfo->pDriver;
	pBatch = pInfo->pBatch;
	pCam = pInfo->pCam;

	deTransform Position(pCam->Position());
	Position.GetIn(camfwd);
	campos = Position.Translation;
	Vec3d_Normalize(camfwd);

	PrevTexture = NULL;
	PrevMaterial = NULL;
	UsedBlankTex = false;

	// enable/disable color writes
	if (!SetStencilState(pDriver, Stencil_UseColor, pInfo->ColorWrite))
		DEBUGRETURN(deFALSE,"RenderBatch: SetStencilState(Stencil_UseColor, pInfo->ColorWrite) failed");
	// enable/disable depth writes
	if (!pDriver->SetRenderState(IdeDriver::RENDER_ZWRITESENABLE, pInfo->DepthWrite))
		DEBUGRETURN(deFALSE, "RenderBatch: RENDER_ZWRITESENABLE(pInfo->DepthWrite) failed");

	// if we either aren't doing separate lighting step, or we are and also have texturing enabled
	if (m_TexturesEnabled)
	{
		m_RenderStats->RenderPasses += 1;
		
		if (!pDriver->SetRenderState(IdeDriver::RENDER_SHADEMODE, IdeDriver::SHADE_GOURAUD))
			DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_SHADEMODE : SHADE_GOURAUD failed");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_AMBIENTLIGHT, 0x00000000))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_AMBIENTLIGHT : 0x00000000 failed");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHABLENDENABLE, deFALSE))
			DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_ALPHABLENDENABLE : deFALSE failed");

		// do the requested render on each vertex buffer
#pragma todo(convert to iterators)
//		t_ObjectTree::iterator it, end = pBatch->ObjectTree.end();
		ptr = pBatch->ObjectTree.GetLeftMost(pObject, NULL);
		while (ptr)
		{
			if (pObject->pSurface)
			{
				Material = pObject->pSurface->GetRenderMaterial();
				Texture = pObject->pSurface->GetRenderTexture();
			}
			else
			{
				Material = pObject->SurfaceDetail.Material;
				Texture = pObject->SurfaceDetail.Texture;
			}
			if (!Material)
			{
				Material = s_BlankMat;
			}
			if (Material != PrevMaterial)
			{
				PrevMaterial = Material;
				if (!pDriver->SetRenderState(Material))
					DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(Material) failed");
				m_RenderStats->MaterialsChanged++;
			}
			if (m_TexturesEnabled && Texture)
			{
				if (Texture != PrevTexture)
				{
					m_RenderStats->TexturesTotal++;
				}
			}
			else
			{
				Texture = s_BlankTex;
				if (!UsedBlankTex)
				{
					UsedBlankTex = true;
					m_RenderStats->TexturesTotal++;
				}
			}

			if (Texture != PrevTexture)
			{
				PrevTexture = Texture;
				m_RenderStats->TexturesChanged++;

				if (!pDriver->SetRenderState(Texture))
					DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(Texture) failed");
			}
			
			ptr2 = ptr;
			while (ptr2)
			{
				if (pObject->IndexOverride)
					TriCount = pObject->IndexOverride->GetTriCount();
				else
					TriCount = pObject->VB->GetTriCount();
				if (TriCount > 0)
				{
					pBatch->Concat.Multiply(pObject->Transform, pObject->Transform);
					if (pObject->SurfaceDetail.Transparency)
					{
						void * ptr3;
						deDouble CurrDepth = 0, Depth;
						Depth = Vec3d_Dot(camfwd, (pObject->Transform.Translation - campos));
						TransObj.Depth = Depth;
						TransObj.RenderObject = pObject;
						ptr3 = Transparents.GetNext(NULL, pTransObj);
						while(ptr3)
						{
							CurrDepth = pTransObj->Depth;
							if (Depth <= CurrDepth)
							{
								if (!Transparents.AddElementAfter(TransObj, ptr3))
									DEBUGRETURN(deFALSE,"RenderGeometry: failed to add an object to the transparents list");
								break;
							}
							
							ptr3 = Transparents.GetNext(ptr3, pTransObj);
						}
						if (!ptr3)
							Transparents.AddElement(TransObj);
					}
					else if (pObject->isSky)
					{
						SkyList.AddElement(pObject);
					}
					else
					{
						pDriver->DisableAllLights();
						for (counter = 0; counter < MAX_LIGHTS_PER_PASS && pObject->Lights[counter]; ++counter)
						{
							pDriver->SetRenderState(pObject->Lights[counter]->GetRenderLight());
						}

						if(!pDriver->SetWorldTransform(&(pObject->Transform)))
						{
							pDriver->EndFrame();
							DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetWorldTransform() failed");
						}
						if(!pDriver->Render(pObject->VB, pObject->IndexOffset, pObject->IndexOverride))
						{
							pDriver->EndFrame();
							DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::Render() failed");
						}

						m_RenderStats->PrimitiveRenders += TriCount;
						TempLong = pObject->VB->GetVertexCount();
						m_RenderStats->VertexPasses += TempLong;
						m_RenderStats->VertexRenders += TempLong;
						m_RenderStats->IndexRenders += pObject->VB->GetIndexCount();
						m_RenderStats->VBufferRenders++;
						if (pObject->VB->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB)
							m_RenderStats->VBufferSubRenders++;
					}
				}
				ptr2 = pBatch->ObjectTree.GetNextChain(ptr2, pObject);
			}
			ptr = pBatch->ObjectTree.GetNextRight(ptr, pObject, NULL);
		}
	}

	ptr = Transparents.GetNext(NULL, pTransObj);
	if (ptr)
	{
		// transparent geometry only reads from depth buffer
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ZWRITESENABLE, deFALSE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ZWRITESENABLE : deFALSE failed - transparency step");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_DEPTHFUNCTION, IdeDriver::COMPARE_LESSEQUAL))
			DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_DEPTHFUNCTION : COMPARE_LESSEQUAL failed");
		// and of course we're using alpha blending based on source texture alpha
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHABLENDENABLE, deTRUE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHABLENDENABLE : deTRUE failed - transparency step");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_SOURCEBLEND, IdeDriver::BLEND_SOURCEALPHA))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_SOURCEBLEND : BLEND_SOURCEALPHA failed - transparency step");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_DESTBLEND, IdeDriver::BLEND_ONE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_DESTBLEND : BLEND_INVERSESOURCEALPHA failed - transparency step");
		// use an alpha test to speed things up on low alpha values
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHATESTENABLE, deTRUE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHATESTENABLE : deTRUE failed - transparency step");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHAREF, 0))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHAREF : 0 failed - transparency step");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHAFUNCTION, IdeDriver::COMPARE_GREATER))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHAFUNCTION : COMPARE_GREATER failed - transparency step");

		if (!pDriver->SetRenderState(IdeDriver::RENDER_HWLIGHTINGENABLE, deFALSE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_HWLIGHTINGENABLE : deFALSE failed - transparency step");

		while (ptr)
		{
			pObject = pTransObj->RenderObject;

			pDriver->DisableAllLights();
			for (counter = 0; counter < MAX_LIGHTS_PER_PASS && pObject->Lights[counter]; ++counter)
			{
				pDriver->SetRenderState(pObject->Lights[counter]->GetRenderLight());
			}
			
			if (pObject->pSurface)
			{
				Texture = pObject->pSurface->GetRenderTexture();
				Material = pObject->pSurface->GetRenderMaterial();
			}
			else
			{
				Texture = pObject->SurfaceDetail.Texture;
				Material = pObject->SurfaceDetail.Material;
			}
			if (!Texture)
			{
				Texture = s_BlankTex;
			}
			if (Texture != PrevTexture)
			{
				pDriver->SetRenderState(Texture);
				m_RenderStats->TexturesChanged++;
				PrevTexture = Texture;
			}

			if (!Material)
			{
				Material = s_BlankMat;
			}
			if (!pDriver->SetRenderState(Material))
				DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(Material) failed");

			if (pObject->IndexOverride)
				TriCount = pObject->IndexOverride->GetTriCount();
			else
				TriCount = pObject->VB->GetTriCount();
			m_RenderStats->PrimitiveRenders += TriCount;
			m_RenderStats->VertexRenders += pObject->VB->GetVertexCount();
			m_RenderStats->IndexRenders += pObject->VB->GetIndexCount();
			m_RenderStats->VBufferRenders++;
			if (pObject->VB->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB)
				m_RenderStats->VBufferSubRenders++;
			if(!pDriver->SetWorldTransform(&(pObject->Transform)))
			{
				pDriver->EndFrame();
				DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetWorldTransform() failed - transparency step");
			}
			if (pObject->SurfaceDetail.Material)
			{
				if(!pDriver->SetRenderState(pObject->SurfaceDetail.Material))
				{
					pDriver->EndFrame();
					DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(pObject->Mat) failed - transparency step");
				}
			}
			else
			{
				if(!pDriver->SetRenderState(s_BlankMat))
				{
					pDriver->EndFrame();
					DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(s_BlankMat) failed - transparency step");
				}
			}
			if(!pDriver->Render(pObject->VB, pObject->IndexOffset, pObject->IndexOverride))
			{
				pDriver->EndFrame();
				DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::Render() failed - transparency step");
			}
			ptr = Transparents.GetNext(ptr, pTransObj);
		}

		if (!pDriver->SetRenderState(IdeDriver::RENDER_HWLIGHTINGENABLE, deTRUE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_HWLIGHTINGENABLE : deTRUE failed - transparency step");
	}
	
	// if we are doing the 2-pass z-buffer-disabled wireframe mode...
	// this is really a special debug thing, shouldn't be used for more than that
	if (m_RenderMeshOverFill)
	{
		long light_state, z_state, z_bias;
		pDriver->SetRenderState(s_BlankTex);
		PrevTexture = NULL;
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHABLENDENABLE, deFALSE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHABLENDENABLE : deFALSE failed - mesh step");
		if (!pDriver->SetRenderState(IdeDriver::RENDER_ALPHATESTENABLE, deFALSE))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_ALPHATESTENABLE : deFALSE failed - mesh step");
		if (pDriver->SetRenderState(IdeDriver::RENDER_FILLMODE, IdeDriver::FILL_WIREFRAME))
			DEBUGRETURN(deFALSE,"RenderGeometry: RENDER_FILLMODE : FILL_WIREFRAME failed - mesh step");
		z_state = pDriver->GetRenderState(IdeDriver::RENDER_ZENABLE);
		light_state = pDriver->GetRenderState(IdeDriver::RENDER_HWLIGHTINGENABLE);
		z_bias = pDriver->GetRenderState(IdeDriver::RENDER_DEPTHBIAS);
		pDriver->SetRenderState(IdeDriver::RENDER_ZENABLE, deFALSE);
		pDriver->SetRenderState(IdeDriver::RENDER_HWLIGHTINGENABLE, deFALSE);
		pDriver->SetRenderState(IdeDriver::RENDER_DEPTHBIAS, 1);
		ptr = pBatch->ObjectTree.GetLeftMost(pObject, NULL);
		while (ptr)
		{
			ptr2 = ptr;
			while (ptr2)
			{
				if (pObject->IndexOverride)
					TriCount = pObject->IndexOverride->GetTriCount();
				else
					TriCount = pObject->VB->GetTriCount();
				if (TriCount > 0)
				{
					if(!pDriver->SetWorldTransform(&(pObject->Transform)))
					{
						pDriver->EndFrame();
						DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetWorldTransform() failed - meshoverfill");
					}
					if(!pDriver->Render(pObject->VB, pObject->IndexOffset, pObject->IndexOverride))
					{
						pDriver->EndFrame();
						DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::Render() failed - meshoverfill");
					}
				}
				ptr2 = pBatch->ObjectTree.GetNextChain(ptr2, pObject);
			}
			ptr = pBatch->ObjectTree.GetNextRight(ptr, pObject, NULL);
		}
		// reset it to solid fill mode next frame
		pDriver->SetRenderState(IdeDriver::RENDER_ZENABLE, z_state);
		pDriver->SetRenderState(IdeDriver::RENDER_HWLIGHTINGENABLE, light_state);
		pDriver->SetRenderState(IdeDriver::RENDER_DEPTHBIAS, z_bias);
	}

	// sky!
	pDriver->SetDepthRange(0.99999f, 1.0);
	if (!pDriver->SetRenderState(s_BlankMat))
		DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(s_BlankMat) failed");
	s32 fogstate = pDriver->GetRenderState(IdeDriver::RENDER_FOGENABLE);
	if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGENABLE, 0))
		DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGENABLE : 0 failed");
	ptr = SkyList.GetNext(NULL, pObject);
	while (ptr)
	{
		m_RenderStats->VertexPasses += pObject->VB->GetVertexCount();
		m_RenderStats->TexturesTotal++;
		m_RenderStats->TexturesChanged++;
		if (!pDriver->SetRenderState(pObject->SurfaceDetail.Texture))
			DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetRenderState(Texture) failed");
		if(!pDriver->SetWorldTransform(&(pObject->Transform)))
		{
			pDriver->EndFrame();
			DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::SetWorldTransform() failed");
		}
		if(!pDriver->Render(pObject->VB, pObject->IndexOffset, pObject->IndexOverride))
		{
			pDriver->EndFrame();
			DEBUGRETURN(deFALSE,"RenderGeometry: IdeDriver::Render() failed");
		}
		ptr = SkyList.GetNext(ptr, pObject);
	}
	pDriver->SetDepthRange(0.0, 1.0);
	if (!pDriver->SetRenderState(IdeDriver::RENDER_FOGENABLE, fogstate))
		DEBUGRETURN(deFALSE, "RenderGeometry: RENDER_FOGENABLE : 0 failed");
	
	return deTRUE;
}

deBoolean deRender::SetSubViewport(IdeDriver * pDriver, IdeCamera * cam, deRect SubRect)
{
	if (!pDriver)
		DEBUGRETURN(deFALSE,"SetSubViewport: specified parameter was NULL: 'pDriver'");
	if (!cam)
		DEBUGRETURN(deFALSE,"SetSubViewport: specified parameter was NULL: 'cam'");
	
	deRect CamRect;
	deFloatRect	FSubRect;
	int Width, Height;
	deDouble Aspect;
	deFloat Near, Far;
	
	// set the viewport to the sub-rect
	Width = SubRect.Right - SubRect.Left;
	Height = SubRect.Bottom - SubRect.Top;
	
	Near = deFloat(cam->GetNearClip());
	Far = deFloat(cam->GetFarClip());
	
	if (!pDriver->SetViewport(SubRect.Left, SubRect.Top, Width, Height, 0, 1))
		DEBUGRETURN(deFALSE,"SetSubViewport: IdeDriver::SetViewport() failed");
	
	// set the projection transformation up for the sub-rect
	CamRect = cam->GetRect();
	
	Width = CamRect.Right - CamRect.Left;
	Height = CamRect.Bottom - CamRect.Top;
	
	Aspect = ((deDouble)Width)/Height;
	
	FSubRect.Top = 1.0f - (deFloat(SubRect.Top - CamRect.Top) / Height);
	FSubRect.Left = (deFloat(SubRect.Left - CamRect.Left) / Width);
	FSubRect.Right = 1.0f - (deFloat(CamRect.Right - SubRect.Right) / Width);
	FSubRect.Bottom = (deFloat(CamRect.Bottom - SubRect.Bottom) / Height);
	
	if (cam->IsOrtho())
	{
		float OrthoWidth = (float)(cam->GetOrthoWidth());
		float OrthoHeight = (float)(OrthoWidth / Aspect);
		if (!pDriver->SetOrthoClippingSubRectFloat(OrthoWidth, OrthoHeight, Near, Far, deFALSE, FSubRect))
			DEBUGRETURN(deFALSE,"SetSubViewport: IdeDriver::SetOrthoClippingSubRect() failed");
	}
	else
	{
		if (!pDriver->SetFOVClippingSubRectFloat(float(cam->GetFOVy(Aspect)), float(Aspect), Near, Far, FSubRect))
			DEBUGRETURN(deFALSE,"SetSubViewport: IdeDriver::SetFOVClippingSubRect() failed");
	}
	
	return deTRUE;
}

deBoolean deRender::SetScissorRect(IdeDriver* pDriver, deRect ScissorRect)
{
	return pDriver->SetScissorRect(ScissorRect);
}

deBoolean deRender::ClearBackground(HWND hWnd, deARGB color, deBoolean ClearZ)
{
	IdeDriver::deDisplay	Display;
	IdeDriver*	pDriver;
	deRect		Viewport;
	
	pDriver = GetDriver(hWnd);
	Display = *(pDriver->GetDisplay());
	Viewport.Left = 0;
	Viewport.Top = 0;
	Viewport.Right = Display.Width;
	Viewport.Bottom = Display.Height;
	
	if (!pDriver->SetViewport(0, 0, Display.Width, Display.Height, 0, 1))
	{
		DEBUGRETURN(deFALSE,"ClearBackground: IdeDriver::SetViewport() failed");
	}
	
	if(!pDriver->ClearFrame(color, DE_TRUE, ClearZ, ClearZ))
	{
		DEBUGRETURN(deFALSE,"ClearBackground: IdeDriver::ClearFrame() failed");
	}
	
	return DE_TRUE;
}

deBoolean deRender::Screenshot(HWND hWnd, const char* filename, long ScreenSizes)
{
	if (ScreenSizes > 5)
		DEBUGRETURN(deFALSE, "Screenshot: Too big of an image");
	if (ScreenSizes < 1)
		DEBUGRETURN(deFALSE, "Screenshot: Non-positive image size");
	IdeDriver * pDriver = GetDriver(hWnd);
	IdeBitmap * pBMP = NULL;
	pDriver->ScreenShot(pBMP, deTRUE);
	if (pBMP)
	{
		pBMP->SaveToFileSystem(NULL, filename);
		pBMP->Release();
		return deTRUE;
	}
	return deFALSE;
}

