/*!
	@file RuSceneBase.h

	Copyright (c) 2004-2005 All rights reserved
*/

#ifndef _RUSCENEBASE_H_
#define _RUSCENEBASE_H_

#include "../../Engine/Texture/RuTextureAtlas_Aggregate.h"

#include "../../Scene/Base/RuEntityBase.h"
#include "../../Scene/Base/RuEntityACT.h"
#include "../../Scene/Base/RuHierarchy_GR2.h"
#include "../../Scene/Container/RuContainer_Portal.h"
#include "../../Scene/Container/RuEntityAABBTree.h"
#include "../../Scene/Occlusion/RuEntity_AntiPortal.h"

#pragma unmanaged

/*
	NOTE
	----

	There is a "leak" with lighting info. Right now we just keep on creating a new lighting info
	structure for each new entity. So.... if entities keep getting paged in and out, we get massive
	memory allocation just for these structures. Normally there needs to be a garbage collection
	phase which collects the lighting info from entities that are no longer active!
*/

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

class IRuSceneRenderer;

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

enum RuRendererCommand
{
	ruRENDERERCMD_RTCOPY						= 0,				//!< Fast RT-to-RT copy
	ruRENDERERCMD_TEXCOPY						= 1,				//!< Slow texture copy with conversion
	ruRENDERERCMD_FLUSH							= 2,				//!< Renderer buffer flush
	ruRENDERERCMD_EXECUTE_NOSORT				= 3,				//!< No-sorting execution
	ruRENDERERCMD_EXECUTE_SINGLE				= 4,				//!< Single-entry execution
	ruRENDERERCMD_SCISSOR_CLEAR					= 5,
	ruRENDERERCMD_SCISSOR_SET					= 6,
	ruRENDERERCMD_VIEWPORT_SET					= 7,
	ruRENDERERCMD_VIEWPORT_CLEAR				= 8,
	ruRENDERERCMD_RENDERTARGET_SET				= 9,

	ruRENDERERCMD_FORCE_DWORD					= 0x7FFFFFFF
};

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

/*!
	Enumeration of effect types supported by the 2D toolkit.
*/
enum Ru2D_EffectType
{
	ru2DFXTYPE_NULL								= 0,				//!< Invalid effect type
	ru2DFXTYPE_FLAT								= 1,				//!< Diffuse color only, blended using SRCALPHA:INVSRCALPHA
	ru2DFXTYPE_FLAT_MASKED						= 2,				//!< Diffuse color only, blended using SRCALPHA:INVSRCALPHA
	ru2DFXTYPE_BLIT								= 3,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA
	ru2DFXTYPE_BLIT_ADD							= 4,				//!< Image transfer modulated by color, blended using ONE:ONE
	ru2DFXTYPE_BLIT_FILTER						= 5,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA, with linear filtering
	ru2DFXTYPE_BLIT_FILTER_CUBIC				= 6,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA, with cubic filtering
	ru2DFXTYPE_BLIT_MASK						= 7,				//!< 
	ru2DFXTYPE_BLIT_MASKED						= 8,				//!< 
	ru2DFXTYPE_BLIT_MASK_Z						= 9,				//!< Image transfer as a Z mask, texels that pass alpha test are set with Z = 0.0
	ru2DFXTYPE_BLIT_LUMINANCE					= 10,				//!< Image transfer using luminance only modulated by color, blended using SRCALPHA:INVSRCALPHA, with linear filtering
	ru2DFXTYPE_TEXTBLIT_FILTER					= 11,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA, with linear filtering
	ru2DFXTYPE_DARKEN							= 12,				//!< Area darkening with strength specified in ru2DFXPARAM_STRENGTH
	ru2DFXTYPE_CUTOFF							= 13,				//!< Cutoff with cutoff threshold specified in ru2DFXPARAM_STRENGTH
	ru2DFXTYPE_EXPONENTIATE						= 14,				//!< Exponentiation with power specified in ru2DFXPARAM_STRENGTH
	ru2DFXTYPE_GAUSSIAN							= 15,				//!< Gaussian filter
	ru2DFXTYPE_MODULATE							= 16,				//!< Modulation
	ru2DFXTYPE_SPLAT_3							= 17,				//!< [TERRAIN RENDERER] PS1.1 3-layer blend (deprecated)
	ru2DFXTYPE_SPLAT_6							= 18,				//!< [TERRAIN RENDERER] PS2.0 6-layer blend (deprecated)
	ru2DFXTYPE_FORCE_DWORD						= 0x7FFFFFFF
};

/*!
	Enumeration of effect parameters.
*/
enum Ru2D_EffectParameter
{
	ru2DFXPARAM_STRENGTH						= 0,				//!< Effect strength, parameter is a DWORD in the range [0, 100]
	ru2DFXPARAM_COLOR							= 1,				//!< Effect color, parameter is a RUCOLOR value (0xAARRGGBB)
	ru2DFXPARAM_ROTATE							= 2,				//!< Rotation, parameter is a Ru2D_RotateMode value
	ru2DFXPARAM_STENCILID						= 3,				//!< Stencil ID, parameter is a UINT8
	ru2DFXPARAM_FORCE_DWORD						= 0x7FFFFFFF
};

/*!
	Enumeration of rotate modes.
*/
enum Ru2D_RotateMode
{
	ru2DROTATE_NONE								= 0,				//!< No rotation
	ru2DROTATE_CLOCKWISE_90						= 1,				//!< Clockwise 90 degrees
	ru2DROTATE_CLOCKWISE_180					= 2,				//!< Clockwise 180 degrees
	ru2DROTATE_CLOCKWISE_270					= 3,				//!< Clockwise 270 degrees
	ru2DROTATE_FLIP_U							= 4,				//!< Flip U axis
	ru2DROTATE_FLIP_V							= 5,				//!< Flip V axis
	ru2DROTATE_FORCE_DWORD						= 0x7FFFFFFF
};

/*!
	Enumeration of uv coordinate modes for 2D output.
*/
enum Ru2D_UVMode
{
	ru2DUVMODE_OFFSET							= 0,				//!< UV coordinates are offset according to rasterization rules
	ru2DUVMODE_DIRECT							= 1,				//!< UV coordinates are fed as-is

	ru2DUVMODE_FORCE_DWORD						= 0x7FFFFFFF
};

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

class CRuRendererOptions
{
public:
	BOOL									m_shadow_Enable;				//!< Shadowing enabled?
	INT32									m_shadow_CasterLevel;			//!< Shadow caster detail level [0, 3]
	INT32									m_shadow_ReceiverLevel;			//!< Shadow receiver detail level [0, 3]
	REAL									m_shadow_Fade_Start;			//!< Maximum shadow display distance
	REAL									m_shadow_Fade_End;				//!< Maximum shadow display distance
	REAL									m_shadow_Cutoff;				//!< Maximum shadow cutoff distance
	REAL									m_shadow_Strength;				//!< Shadow strength

	REAL									m_terrain_TextureDistance;		//!< Maximum distance to texture the terrain to
	BOOL									m_terrain_Alternate;			//!< Alternate terrain rendering mode

public:
											CRuRendererOptions();
};

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

struct RuBroadcast_Submitted_Args
{
	IRuSceneRenderer*						m_renderer;						//!< Renderer
	CRuCamera*								m_camera;						//!< Camera used for submission

	BOOL									m_continueSubmission;			//!< Return value: Continue with render submission for this entry?
};

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

/*!
	Interface specification for the core scene renderer. The base scene renderer should
	provide lighting prop management capabilities as well as both 2D and 3D submission
	capabilities.

	@author John Tang
	@version 2004/12/13
*/
class IRuSceneRenderer
{
public:
	typedef boost::function<IRuMaterial * (IRuMaterial *)> MaterialOverrideCallback;		//!< Defined: IRuMaterial* MaterialOverrideCallback(IRuMaterial *sourceMaterial);

public:
	virtual									~IRuSceneRenderer();

	// Event system
	virtual BOOL							RegisterListener(IRuEntityEventListener *listener, void *userData) = 0;
	virtual BOOL							UnregisterListener(IRuEntityEventListener *listener) = 0;

	virtual void							EnableRendering(BOOL enableRendering) = 0;
	virtual void							Debug_ToggleNullRendering() = 0;

	// Statistics query
	virtual UINT32							GetNumPrimitivesRendered() = 0;
	virtual UINT32							GetNumBatchesIssued() = 0;

	// Performance analysis
	virtual BOOL							RecordPerformanceProfile(IRuStream *outputStream) = 0;

	// Manual camera setup
	virtual CRuCamera*						GetCamera() = 0;
	virtual void							SetCamera(CRuCamera *camera) = 0;

	// Light prop setup
	virtual void							SetLightingProp(CRuEntity *lightingProp) = 0;
	virtual void							SetSpecularColor(DWORD specularColor) = 0;
	virtual void							SetSpecularDirection(const CRuVector3 &specularDir) = 0;

	virtual CRuParameterBlock*				GetShaderParameterBlock() = 0;

	// Common
	virtual void							Begin() = 0;
	virtual void							End() = 0;
	
	virtual UINT8							GetNextStencilID() = 0;

	virtual BOOL							PushRenderTarget(IRuObject *renderTarget) = 0;
	virtual BOOL							PopRenderTarget() = 0;

	virtual BOOL							Ru2D_PushViewport(IRuViewport *viewport) = 0;
	virtual BOOL							Ru2D_PopViewport() = 0;
	virtual BOOL							Ru2D_ClearViewport(DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil) = 0;

	// Other stuff
	virtual IRuEngineResource*				GetPrimaryRenderTarget() = 0;
	virtual INT32							GetPrimaryRenderTargetWidth() = 0;
	virtual INT32							GetPrimaryRenderTargetHeight() = 0;

	// Scenegraph rendering
	virtual BOOL							Scene_PushMaterialOverrideCallback(IRuSceneRenderer::MaterialOverrideCallback overrideCallback) = 0;
	virtual BOOL							Scene_PopMaterialOverrideCallback() = 0;
	virtual void							Scene_SetRenderTarget(IRuObject *renderTarget, INT32 priority, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil) = 0;
	virtual BOOL							Scene_PushRenderTarget(IRuObject *renderTarget, INT32 priority, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil) = 0;
	virtual BOOL							Scene_PopRenderTarget() = 0;
	virtual BOOL							Scene_PushViewport(IRuViewport *viewport, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil) = 0;
	virtual BOOL							Scene_PopViewport() = 0;
	virtual BOOL							Scene_PushClipPlane(INT32 index, const CRuPlane4 &plane) = 0;
	virtual BOOL							Scene_PopClipPlane(INT32 index) = 0;
	virtual BOOL							Scene_PushParameterBlock(CRuParameterBlock *paramBlock) = 0;
	virtual BOOL							Scene_PopParameterBlock() = 0;
	virtual BOOL							Scene_PushEventSuppression(RuEntityEvent suppressedEvent) = 0;
	virtual RuEntityEvent					Scene_PopEventSuppression() = 0;
	virtual void							Scene_Submit(CRuEntity *sceneRoot, CRuCamera *camera = NULL, BOOL noStatusMod = FALSE) = 0;
	virtual void							Scene_Submit(CRuEntity *sceneRoot, IRuMesh *mesh, IRuMaterial *material) = 0;
	virtual void							Scene_Flush() = 0;

	virtual void							Ru2D_ConvertCoordinates(CRuVector3 &point) = 0;
	virtual void							Ru2D_ConvertCoordinates(CRuRectangle &rectangle) = 0;
	virtual void							Ru2D_WorldToViewport(CRuCamera *camera, CRuVector3 &point) = 0;
	virtual void							Ru2D_WorldToScreen(CRuCamera *camera, CRuVector3 &point) = 0;
	virtual void							Ru2D_HomogeneousToViewport(CRuVector3 &point) = 0;
	virtual void							Ru2D_HomogeneousToViewport(CRuRectangle &rectangle) = 0;
	virtual void							Ru2D_ViewportToHomogeneous(CRuVector3 &point) = 0;
	virtual void							Ru2D_ViewportToHomogeneous(CRuRectangle &rectangle) = 0;

	// 2D toolkit
	virtual BOOL							Ru2D_SetViewportScale(REAL width, REAL height) = 0;
	virtual BOOL							Ru2D_PushViewportScale(REAL width, REAL height) = 0;
	virtual BOOL							Ru2D_PopViewportScale() = 0;
	virtual IRuTexture*						Ru2D_GetReservedDXT5Texture() = 0;
	virtual BOOL							Ru2D_SetScissorRect(CRuRectangle *scissorRect) = 0;
	virtual BOOL							Ru2D_SetUVMode(Ru2D_UVMode uvMode) = 0;
	virtual BOOL							Ru2D_SetEffectType(Ru2D_EffectType effectType) = 0;
	virtual BOOL							Ru2D_SetEffectParameter(Ru2D_EffectParameter effectParam, DWORD parameter) = 0;
	virtual BOOL							Ru2D_SetShaderParameter(RuParameterType paramType, DWORD param) = 0;
	virtual BOOL							Ru2D_SetTexture(IRuTexture *texture, BOOL allowBinding = TRUE, RuTextureChannel texChannel = ruTEXCHANNEL_DIFFUSEMAP) = 0;
	virtual BOOL							Ru2D_SetRotation(REAL rotation) = 0;
	virtual void							Ru2D_Submit(const CRuRectangle &srcRect, const CRuRectangle &destRect) = 0;
	virtual void							Ru2D_Submit(INT32 numRects, const CRuRectangle *srcRect, const CRuRectangle *destRect, const DWORD *color) = 0;
	virtual void							Ru2D_Flush() = 0;

	// 3D toolkit
	virtual void							Ru3D_Submit(CRuMatrix4x4 *worldTransform, IRuMesh *mesh, IRuMaterial *material) = 0;

	// Renderer toolkit
	virtual void							RuX_CopyTexture(IRuEngineResource *srcTexture, IRuEngineResource *dstTexture) = 0;
	virtual void							RuX_ConvertTexture(IRuEngineResource *srcTexture, IRuEngineResource *dstTexture) = 0;

	// Metric
	virtual const CRuProfiler*				GetSceneProfiler() const = 0;
};

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

extern IRuTexture *reflectTex;
extern IRuTexture *refractTex;
extern INT32 curCausticIndex;

typedef void (*RenderCompleteCallback)(IRuSceneRenderer *sceneRenderer, IRuObject *renderTarget);

/*!
	Implementation of the IRuSceneRenderer interface. Supports automatic batching capabilities
	as well as immediate-mode 2D and 3D toolkits.

	@author John Tang
	@version 2004/12/13
*/
class CRuSceneRenderer : public IRuSceneRenderer
{
protected:
	enum RuRendererState
	{
		ruRENDERERSTATE_STOPPED				= 0,
		ruRENDERERSTATE_2D					= 1,
		ruRENDERERSTATE_3D					= 2,
		ruRENDERERSTATE_3D_IMMEDIATE		= 3
	};

	struct EventListenerDesc
	{
		IRuEntityEventListener*				m_listener;										//!< Event listener
		void*								m_userData;										//!< User-specified listener data
		EventListenerDesc*					m_next;											//!< Pointer to the next event listener descriptor
	};

	struct LightingInfo
	{
		INT32								m_entityIteration;								//!< Iteration number of the target entity
		INT32								m_lightingIteration;							//!< Iteration number of the lighting information
		CRuColor4							m_ambientLight;									//!< Ambient light color
		CRuColor4							m_shCoefficients[9];							//!< Object-level SH lighting coefficients
		CRuVector3							m_specularDirection;							//!< Object-space specular light source position
	};

	struct ViewportDescriptor
	{
		IRuViewport*						m_viewport;										//!< Viewport object
		BOOL								m_cleared;										//!< Has the viewport been cleared?

		DWORD								m_clearFlags;									//!< Viewport clearing flags
		RUCOLOR								m_clearColor;									//!< Color to clear to
		REAL								m_clearZ;										//!< Z to clear to
		DWORD								m_clearStencil;									//!< Stencil value to clear to
	};

	struct RenderTargetDescriptor
	{
		IRuObject*							m_renderTarget;									//!< Render target object
		IRuSurface*							m_depthStencilSurface;							//!< Depth stencil surface, NULL = use default
		INT32								m_priority;										//!< Render target priority
		INT32								m_viewportIndex;								//!< Default viewport index

		BOOL								m_enableClipPlane;
		CRuPlane4							m_clipPlane;

		RenderCompleteCallback				m_renderCompleteCallback;						//!< Callback triggered when all objects have been flushed to the render target, NULL to disable
	};

	struct RTVPTuple
	{
		INT32								m_rtIndex;
		INT32								m_vpIndex;

											RTVPTuple(INT32 rtIndex = 0, INT32 vpIndex = 0)
											:	m_rtIndex(rtIndex),
												m_vpIndex(vpIndex)
											{
											}
	};

	struct ClipPlaneSet
	{
		CRuPlane4							m_clipPlanes[6];
	};

	struct RenderQueueEntry
	{
		// Render target and camera settings
		INT32								m_rtIndex;										//!< Render target to which this entry is rendered
		INT32								m_viewport;										//!< Viewport to use, -1 to disable

		// Clip plane setting
		INT32								m_cpSetIndex;

		// Transformation and parameters
		const CRuMatrix4x4*					m_worldTransform;								//!< World transform
		CRuParameterCache*					m_paramCache;									//!< Entity-specific parameter cache

		// Entity render information
		IRuMesh*							m_mesh;											//!< Mesh
		IRuMaterial*						m_material;										//!< Material
		IRuShader*							m_shader;										//!< Shader to use
		REAL								m_priority;										//!< Entity priority, calculated based on distance to camera and alpha blending settings

		// Other options
		RuTextureChannel					m_dynamicMapType;
		BOOL								m_noReplication;								//!< Disable replication of this entry?

		// Shadow-specific
		UINT32								m_objectID;										//!< Object ID

		CRuEntity*							m_entity;	// debugging only
	};

	struct CommandQueueEntry
	{
		RuRendererCommand					m_command;

		// This might be non-portable...?
		union
		{
			struct a
			{
				IRuEngineResource*			m_srcTexture;									//!< Source texture object
				IRuEngineResource*			m_dstTexture;									//!< Destination texture object
			}								RTCOPY;

			struct b
			{
				INT32						m_flushIdx;										//!< Starting index of the flush
				INT32						m_flushCount;									//!< Number of elements to flush
			}								FLUSH;

			struct c
			{
				INT32						m_executeIdx;									//!< Index of the render queue entry to execute
			}								EXECUTESINGLE;

			struct d
			{
				INT32						m_left;
				INT32						m_top;
				INT32						m_right;
				INT32						m_bottom;
			}								SCISSOR;

			struct e
			{
				IRuViewport*				m_viewport;
			}								VIEWPORTSET;

			struct f
			{
				DWORD						m_clearFlags;
				RUCOLOR						m_clearColor;
				REAL						m_clearZ;
				DWORD						m_clearStencil;
			}								VIEWPORTCLEAR;

			struct g
			{
				IRuObject*					m_renderTarget;
			}								RENDERTARGETSET;
		};
	};

	// Renderer status
	RuRendererState							m_rendererState;

	// Some other crap
	CRuMatrix4x4							m_identityTransform;

	// Settings
	BOOL									m_enableRendering;
	BOOL									m_debug_NullRendering;

	// Event system data
	EventListenerDesc*						m_eventListeners;								//!< Linked list of event listeners

	// ???
	CRuStack<IRuObject *>					m_ru2D_RTStack;
	IRuObject*								m_ru2D_CurrentRenderTarget;

	CRuStack<IRuViewport *>					m_ru2D_ViewportStack;
	IRuViewport*							m_ru2D_CurrentViewport;

	// Current scene state
	INT32									m_iterationNumber;								//!< Iteration number of the current scene
	UINT8									m_nextStencilID;								//!< Next ID to use for mask-stencil operations
	CRuStack<IRuSceneRenderer::MaterialOverrideCallback>	m_materialOverrideCallbackStack;
	IRuSceneRenderer::MaterialOverrideCallback				m_materialOverrideCallback;

	CRuStack<RTVPTuple>						m_rtStack;
	CRuStack<INT32>							m_clipPlaneStack;

//	CRuStack<INT32>							m_rtStack;										//!< Render target push stack
//	CRuStack<INT32>							m_viewportStack;

	CRuArrayList<RuEntityEvent>				m_eventSuppressionList;							//!< List of suppressed events
	INT32									m_primaryRTIndex;								//!< Index to the primary RT (usually the backbuffer)
	INT32									m_curRTIndex;									//!< Index to the currently active RT
	INT32									m_curViewport;
	INT32									m_curCPSetIndex;								//!< Index to the currently active clip plane set, -1 to disable
	UINT32									m_nextObjectID;

	// Core-level objects
	CRuCamera*								m_camera;										// Currently active camera

	CRuParameterBlock						m_paramBlock;

	IRuTexture*								m_nullTexture;									//!< NULL texture map
	IRuTexture*								m_clampTexture;									//!< Clamping texture map
	IRuTexture*								m_cubicFilterLUT;								//!< Cubic filter look-up texture
	IRuSurface*								m_auxDepthStencilSurface;						//!< Auxiliary depth stencil surface

	// 2D toolkit objects
	CRuStack<REAL>							m_ru2D_Viewport_Width_Stack;
	CRuStack<REAL>							m_ru2D_Viewport_Height_Stack;
	REAL									m_ru2D_Viewport_Width;
	REAL									m_ru2D_Viewport_Height;

	IRuTexture*								m_ru2D_32BitAtlasTexture;
	IRuTexture*								m_ru2D_32BitAtlasTexture_Text;
	CRuTextureAtlas_Aggregate*				m_ru2D_TextureAtlas;							//!< Texture atlas for the 2D toolkit

	INT32									m_ru2D_ActiveTextureHandle;
	UINT32									m_ru2D_ActiveTextureWidth;
	UINT32									m_ru2D_ActiveTextureHeight;

	Ru2D_UVMode								m_ru2D_UVMode;
	Ru2D_EffectType							m_ru2D_EffectType;								//!< Currently selected 2D effect type
	DWORD									m_ru2D_DiffuseColor;							//!< Currently used diffuse color
	UINT8									m_ru2D_StencilID;
	REAL									m_ru2D_Rotation;								//!< Current rotation angle in radians
	INT32									m_ru2D_MaxPrimitiveCount;						//!< Maximum number of primitives in the buffer
	INT32									m_ru2D_PrimitiveCount;							//!< Number of primitives in the buffer
	UINT32									m_ru2D_RefRTWidth;								//!< Render target width
	UINT32									m_ru2D_RefRTHeight;								//!< Render target height
	UINT32									m_ru2D_RefTextureWidth;							//!< Texture width used for UV calculation
	UINT32									m_ru2D_RefTextureHeight;						//!< Texture height used for UV calculation

	CRuMaterial*							m_ru2D_RefMaterial;

	CRuMesh*								m_ru2D_Mesh;									//!< Renderer buffer mesh
	CRuArrayList<CRuProxyMesh *>			m_ru2D_ProxyMeshes;
	CRuMaterial*							m_ru2D_Material;								//!< Renderer buffer material
	REAL*									m_ru2D_MaterialConstants;
	CRuRectangle							m_ru2D_Fan_SrcRegion;							//!< Fan stitcher source region
	CRuRectangle							m_ru2D_Fan_DestRegion;							//!< Fan stitcher destination region
	CRuArrayList<CRuVector3>				m_ru2D_VertexList;								//!< Piecewise submission vertex list

	CRuVector3*								m_ru2D_Mesh_Position;
	DWORD*									m_ru2D_Mesh_Diffuse;
	REAL*									m_ru2D_Mesh_UV0;
	UINT16*									m_ru2D_Mesh_Indices;

	// Scene parsing stacks and queues
	INT32									m_scene_FlushIdx;

	INT32									m_selectedRTIndex;
	INT32									m_selectedViewportIndex;
	INT32									m_selectedCPSetIndex;

	CRuStack<CRuEntity *>					m_entityStack;									//!< Stack used for traversing an entity tree
	CRuArrayList<RenderTargetDescriptor>	m_renderTargets;								//!< Render targets used in the current scene
	CRuArrayList<ViewportDescriptor>		m_viewports;									//!< Viewports used in the current scene
	CRuArrayList<ClipPlaneSet>				m_clipPlaneSets;								//!< Clip plane sets used in the current scene
	CRuArrayList<CRuParameterBlock *>		m_extraParameterBlocks;
	CRuArrayList<CommandQueueEntry>			m_commandQueue;									//!< Command queue
	CRuArrayList<RenderQueueEntry>			m_renderQueue;									//!< Rendering queue
	CRuRadixSort							m_radixSort;									//!< Sorting mechanism
	CRuRadixSort							m_radixSort2;									//!< Sorting mechanism

	CRuStack<CRuMatrix4x4 *>				m_usedMatrixStack;								//!< Used temporary matrix stack
	CRuStack<CRuMatrix4x4 *>				m_unusedMatrixStack;							//!< Unused temporary matrix stack

	CRuStack<CRuParameterCache *>			m_usedParameterCacheStack;
	CRuStack<CRuParameterCache *>			m_unusedParameterCacheStack;

	CRuStack<CRuProxyMesh *>				m_usedProxyMeshStack;
	CRuStack<CRuProxyMesh *>				m_unusedProxyMeshStack;

	CRuStack<CRuMaterial *>					m_usedMaterialStack;
	CRuStack<CRuMaterial *>					m_unusedMaterialStack;

	CRuQueue<const CRuParameterBlock*>		m_parameterBlocks;

	// Lighting prop
	CRuAABBTree*							m_lightTree;									//!< Light tree
	CRuArrayList<CRuEntity_Light *>			m_recycledLightEntities;						//!< Unused light structures
	DWORD									m_specularColor;
	CRuVector3								m_specularDirection;

	CRuArrayList<LightingInfo *>			m_activeLightingInfo;

	CRuArrayList<CRuEntity *>				m_enumeratedLights;
	CRuArrayList<REAL>						m_enumeratedLightDistances;

	// Decal
	CRuArrayList<CRuEntity *>				m_validDecalTargets;							//!< List of valid decal targets

	// Occlusion
	CRuArrayList<CRuEntity_AntiPortal *>	m_antiPortals;

	// Shadowing
	CRuCamera*								m_altCamera;
	CRuCamera*								m_shadowCamera;
	CRuMaterial*							m_shadowMaterial;
	CRuMaterial*							m_shadowMapMaterial;

	// Skinning cache
	class CachedSkin
	{
	public:
		DWORD								m_lastUpdateTime;								//!< Time of last skinning update, in milliseconds
		CRuSkinnableMesh*					m_mesh;											//!< Cached skinnable mesh

		CRuArrayList<IRuMesh *>*			m_subdividedMeshes;								//!< List of sub-divided meshes
	};

	CRuHashMap<CRuGUID, CachedSkin>*		m_skinningCache;
	CRuArrayList<CRuGUID>					m_gcSkinningCache;

	// Parameter cache
	CRuParameterCache						m_defaultParamCache;							// Cache for frame-invariant default parameters
	CRuParameterCache						m_paramCache0;									// Outer loop parameter cache
	CRuParameterCache						m_paramCache1;									// Inner loop parameter cache

	// 3rd-order spherical harmonic constants
	REAL									m_sh9_Const1;
	REAL									m_sh9_Const2;
	REAL									m_sh9_Const3;
	REAL									m_sh9_Const4;
	REAL									m_sh9_Const5;

	// Sorting helper
	CRuArrayList<UINT32>					m_sortCriterias[9];								// Sorting criteria

	/*
		New sorting criteria
		--------------------
		1. Render target priority
		2. Render target <-- eliminate?
		3. Shader priority
		4. Shader <-- eliminate?
		5. Distance to camera
		6. Texture 0
		7. Texture 1
	*/

	// Statistics
	UINT32									m_numPrimitivesRendered;						//!< Total number of rendered primitives this frame
	UINT32									m_numBatchesIssued;								//!< Total number of batches issued this frame
	INT32									m_probe_2D_Submit;
	INT32									m_probe_2D_BatchSize;
	INT32									m_probe_2D_BatchCount;
	INT32									m_probe_2D_Flush;
	INT32									m_probe_3D_Submit;
	INT32									m_probe_3D_BatchCount;
	INT32									m_probe_3D_Flush;
	INT32									m_probe_3D_Skinning;
	INT32									m_probe_3D_Morphing;
	INT32									m_probe_3D_Sorting;
	INT32									m_probe_3D_Parameters;
	INT32									m_probe_3D_Lighting;
	INT32									m_probe_3D_TransformSetup;
	INT32									m_probe_3D_ParameterSetup;
	INT32									m_probe_3D_TextureSetup;
	INT32									m_probe_3D_RenderAllPasses;
	INT32									m_probe_3D_Render;
	INT32									m_probe_3D_RenderD3D;
	INT32									m_probe_Engine_InstancingTime;
	INT32									m_probe_Misc_AntiPortalDiscarded;

	// Performance query
	IRuStream*								m_perfProfileStream;
	IDirect3DQuery9*						m_query;

public:
											CRuSceneRenderer();
	virtual									~CRuSceneRenderer();

	// Event system
	virtual BOOL							RegisterListener(IRuEntityEventListener *listener, void *userData);
	virtual BOOL							UnregisterListener(IRuEntityEventListener *listener);
	void									RaiseEvent(CRuEntity *entity, IRuObject *eventProxy, INT32 eventID, PTRVALUE eventParameter0, PTRVALUE eventParameter1, const char *eventString, REAL eventDuration, BOOL propagateEvent = TRUE);

	// Settings
	virtual void							EnableRendering(BOOL enableRendering);
	virtual void							Debug_ToggleNullRendering();

	// Statistics query
	virtual UINT32							GetNumPrimitivesRendered();
	virtual UINT32							GetNumBatchesIssued();

	// Performance analysis
	virtual BOOL							RecordPerformanceProfile(IRuStream *outputStream);

	// Manual camera setup
	virtual CRuCamera*						GetCamera();
	virtual void							SetCamera(CRuCamera *camera);

	// Light prop setup
	virtual void							SetLightingProp(CRuEntity *lightingProp);
	virtual void							SetSpecularColor(DWORD specularColor);
	virtual void							SetSpecularDirection(const CRuVector3 &specularDir);

	virtual CRuParameterBlock*				GetShaderParameterBlock();

	// Common stuff
	virtual void							Begin();
	virtual void							End();

	virtual UINT8							GetNextStencilID();

	virtual BOOL							PushRenderTarget(IRuObject *renderTarget);
	virtual BOOL							PopRenderTarget();

	virtual BOOL							Ru2D_PushViewport(IRuViewport *viewport);
	virtual BOOL							Ru2D_PopViewport();
	virtual BOOL							Ru2D_ClearViewport(DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);

	// Other stuff
	virtual IRuEngineResource*				GetPrimaryRenderTarget();
	virtual INT32							GetPrimaryRenderTargetWidth();
	virtual INT32							GetPrimaryRenderTargetHeight();

	// Scenegraph rendering
	INT32									CreateViewport(IRuViewport *viewport, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);

	virtual BOOL							Scene_PushMaterialOverrideCallback(IRuSceneRenderer::MaterialOverrideCallback overrideCallback);
	virtual BOOL							Scene_PopMaterialOverrideCallback();
	virtual void							Scene_SetRenderTarget(IRuObject *renderTarget, INT32 priority, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);
	virtual BOOL							Scene_PushRenderTarget(IRuObject *renderTarget, INT32 priority, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);
	virtual BOOL							Scene_PopRenderTarget();
	virtual BOOL							Scene_PushViewport(IRuViewport *viewport, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);
	virtual BOOL							Scene_PopViewport();
	virtual BOOL							Scene_PushClipPlane(INT32 index, const CRuPlane4 &plane);
	virtual BOOL							Scene_PopClipPlane(INT32 index);
	virtual BOOL							Scene_PushParameterBlock(CRuParameterBlock *paramBlock);
	virtual BOOL							Scene_PopParameterBlock();
	virtual BOOL							Scene_PushEventSuppression(RuEntityEvent suppressedEvent);
	virtual RuEntityEvent					Scene_PopEventSuppression();
	virtual void							Scene_Submit(CRuEntity *sceneRoot, CRuCamera *camera = NULL, BOOL noStatusMod = FALSE);
	virtual void							Scene_Submit(CRuEntity *sceneRoot, IRuMesh *mesh, IRuMaterial *material);
	virtual void							Scene_Flush();
	virtual void							Scene_Flush_NULL();

	void									ExecuteCommandQueue();
	void									ExecuteRenderQueue(INT32 flushIdx, INT32 flushCount, BOOL sortRenderQueue);

	static void								Shadow_RenderCompleteCallback(IRuSceneRenderer *sceneRenderer, IRuObject *renderTarget);

	// Immediate rendering

	// 2D toolkit
	virtual BOOL							Ru2D_SetViewportScale(REAL width, REAL height);
	virtual BOOL							Ru2D_PushViewportScale(REAL width, REAL height);
	virtual BOOL							Ru2D_PopViewportScale();
	virtual IRuTexture*						Ru2D_GetReservedDXT5Texture();
	virtual BOOL							Ru2D_SetScissorRect(CRuRectangle *scissorRect);
	virtual BOOL							Ru2D_SetUVMode(Ru2D_UVMode uvMode);
	virtual BOOL							Ru2D_SetEffectType(Ru2D_EffectType effectType);
	virtual BOOL							Ru2D_SetEffectParameter(Ru2D_EffectParameter effectParam, DWORD parameter);
	virtual BOOL							Ru2D_SetShaderParameter(RuParameterType paramType, DWORD param);
	virtual BOOL							Ru2D_SetTexture(IRuTexture *texture, BOOL allowBinding = TRUE, RuTextureChannel texChannel = ruTEXCHANNEL_DIFFUSEMAP);
	virtual BOOL							Ru2D_SetRotation(REAL rotation);
	virtual void							Ru2D_Submit(const CRuRectangle &srcRect, const CRuRectangle &destRect);
	virtual void							Ru2D_Submit(INT32 numRects, const CRuRectangle *srcRect, const CRuRectangle *destRect, const DWORD *color);
	virtual void							Ru2D_Flush();

	virtual void							Ru2D_ConvertCoordinates(CRuVector3 &point);
	virtual void							Ru2D_ConvertCoordinates(CRuRectangle &rectangle);
	virtual void							Ru2D_WorldToViewport(CRuCamera *camera, CRuVector3 &point);
	virtual void							Ru2D_WorldToScreen(CRuCamera *camera, CRuVector3 &point);
	virtual void							Ru2D_HomogeneousToViewport(CRuVector3 &point);
	virtual void							Ru2D_HomogeneousToViewport(CRuRectangle &rectangle);
	virtual void							Ru2D_ViewportToHomogeneous(CRuVector3 &point);
	virtual void							Ru2D_ViewportToHomogeneous(CRuRectangle &rectangle);

	// 3D toolkit
	virtual void							Ru3D_Submit(CRuMatrix4x4 *worldTransform, IRuMesh *mesh, IRuMaterial *material);

	// Renderer toolkit
	virtual void							RuX_CopyTexture(IRuEngineResource *srcTexture, IRuEngineResource *dstTexture);
	virtual void							RuX_ConvertTexture(IRuEngineResource *srcTexture, IRuEngineResource *dstTexture);

	// Metric
	virtual const CRuProfiler*				GetSceneProfiler() const;

protected:
public:
	CRuMatrix4x4*							CreateMatrix();
	void									FlushMatrixStack();

	CRuParameterCache*						CreateParameterCache();
	void									FlushParameterCacheStack();

	CRuMaterial*							Ru2D_AllocateMaterial();
	void									Ru2D_FlushMaterialStack();

	void									GarbageCollectSkinningCache();
	BOOL									GarbageCollectSkinningCacheCallback(const void *key, void *data);

	void									FlushSkinningCache();
	BOOL									FlushSkinningCacheCallback(const void *key, void *data);

	void									AddtoSH9(CRuColor4 *shCoefficients, CRuColor4 &lightColor, CRuVector3 &dirN);
	LightingInfo*							BuildLightingInfo(IRuEntity_Renderable *renderableEntity);

	CommandQueueEntry&						Scene_AllocateCommandQueueEntry();
	RenderQueueEntry&						Scene_AllocateRenderQueueEntry();
	void									Scene_UndoAllocateRenderQueueEntry();
	void									Scene_Submit_Internal(CRuEntity *sceneRoot, CRuMatrix4x4 const *worldTransform, IRuMesh *mesh, IRuMaterial *material, CRuCamera *camera, BOOL firstEntityInSeries);
	void									Scene_SortRenderQueue(INT32 flushIdx = -1, INT32 flushCount = -1);

	void									Ru2D_RenderBatch();

	BOOL									IsEventSuppressed(RuEntityEvent entityEvent);

	BOOL									TransitionRendererState(CRuSceneRenderer::RuRendererState newRendererState);
	BOOL									TransitionRendererState_Backend(CRuSceneRenderer::RuRendererState newRendererState);
	BOOL									SelectRenderTarget(IRuObject *renderTarget);
};

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

__forceinline CRuSceneRenderer::CommandQueueEntry& CRuSceneRenderer::Scene_AllocateCommandQueueEntry()
{
	INT32 allocatedIndex = m_commandQueue.Count();

	if(m_commandQueue.SetNumEntries(allocatedIndex + 1) == FALSE)
	{
		m_commandQueue.Add(CommandQueueEntry());
	}

	return m_commandQueue[allocatedIndex];
}

__forceinline CRuSceneRenderer::RenderQueueEntry& CRuSceneRenderer::Scene_AllocateRenderQueueEntry()
{
	INT32 allocatedIndex = m_renderQueue.Count();

	if(m_renderQueue.SetNumEntries(allocatedIndex + 1) == FALSE)
	{
		m_renderQueue.Add(RenderQueueEntry());
	}

	return m_renderQueue[allocatedIndex];
}

__forceinline void CRuSceneRenderer::Scene_UndoAllocateRenderQueueEntry()
{
	if(m_renderQueue.Count() > 0)
	{
		m_renderQueue.SetNumEntries(m_renderQueue.Count() - 1);
	}
}

__forceinline BOOL CRuSceneRenderer::TransitionRendererState(CRuSceneRenderer::RuRendererState newRendererState)
{
	// New state is the same as the existing state, simply return
	if(m_rendererState == newRendererState)
	{
		return TRUE;
	}

	return TransitionRendererState_Backend(newRendererState);
}

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

extern RUAPI CRuRendererOptions				g_ruRendererOptions;
extern RUAPI IRuSceneRenderer*				g_ruSceneRenderer;

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

#pragma managed

#endif
