/*!
	@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/Container/RuContainer_Portal.h"
#include "../../Scene/Container/RuEntityAABBTree.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!
*/

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

/*!
	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_BLIT								= 2,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA
	ru2DFXTYPE_BLIT_ADD							= 3,				//!< Image transfer modulated by color, blended using ONE:ONE
	ru2DFXTYPE_BLIT_FILTER						= 4,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA, with linear filtering
	ru2DFXTYPE_BLIT_FILTER_CUBIC				= 5,				//!< Image transfer modulated by color, blended using SRCALPHA:INVSRCALPHA, with cubic filtering
	ru2DFXTYPE_BLIT_MASK						= 6,				//!< 
	ru2DFXTYPE_BLIT_MASKED						= 7,				//!< 
	ru2DFXTYPE_BLIT_LUMINANCE					= 8,				//!< Image transfer using luminance only modulated by color, blended using SRCALPHA:INVSRCALPHA, with linear filtering
	ru2DFXTYPE_DARKEN							= 9,				//!< Area darkening with strength specified in ru2DFXPARAM_STRENGTH
	ru2DFXTYPE_GAUSSIAN							= 10,				//!< Gaussian filter
	ru2DFXTYPE_MODULATE							= 11,				//!< Modulation
	ru2DFXTYPE_SPLAT_3							= 12,				//!< [TERRAIN RENDERER] PS1.1 3-layer blend
	ru2DFXTYPE_SPLAT_6							= 13,				//!< [TERRAIN RENDERER] PS2.0 6-layer blend
	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_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
};

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

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();
};

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

/*!
	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:
	virtual									~IRuSceneRenderer();

	// System linkup
	virtual IRuInstanceManager*				GetInstanceManager() = 0;
	virtual BOOL							SetInstanceManager(IRuInstanceManager *instanceManager) = 0;

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

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

	// Manual camera setup
	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;

	// Scenegraph rendering
	virtual void							Scene_Begin() = 0;
	virtual void							Scene_SetRenderTarget(IRuObject *renderTarget, INT32 priority, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil) = 0;
	virtual void							Scene_Submit(CRuEntity *sceneRoot, CRuCamera *camera = NULL) = 0;
	virtual void							Scene_Submit(CRuMatrix4x4 *worldTransform, IRuMesh *mesh, IRuMaterial *material) = 0;
	virtual void							Scene_Flush() = 0;
	virtual void							Scene_End() = 0;

	// 2D toolkit
	virtual BOOL							Ru2D_Begin() = 0;
	virtual BOOL							Ru2D_End() = 0;
	virtual BOOL							Ru2D_SetViewportScale(REAL width, REAL height) = 0;
	virtual IRuTexture*						Ru2D_GetReservedDXT5Texture() = 0;
	virtual BOOL							Ru2D_SetScissorRect(CRuRectangle *scissorRect) = 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, RuTextureChannel texChannel = ruTEXCHANNEL_DIFFUSEMAP) = 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_Fan_Begin(const CRuRectangle &srcRect, const CRuRectangle &destRect) = 0;
	virtual void							Ru2D_Fan_End() = 0;
	virtual void							Ru2D_Fan_Submit(const CRuVector3 &fanVertex) = 0;
	virtual void							Ru2D_Line_Begin() = 0;
	virtual void							Ru2D_Line_End() = 0;
	virtual void							Ru2D_Line_Submit(const CRuVector3 &lineVertex) = 0;
	virtual void							Ru2D_Line_Submit(const CRuVector3 &lineVertex0, const CRuVector3 &lineVertex1) = 0;
	virtual void							Ru2D_Line_Submit(INT32 numLines, const CRuVector3 *lineVertices, const DWORD *color) = 0;

	// 3D toolkit
	virtual BOOL							Ru3D_Begin() = 0;
	virtual BOOL							Ru3D_End() = 0;
	virtual BOOL							Ru3D_SetShader(IRuShader *shader) = 0;
	virtual BOOL							Ru3D_SetShaderParameter(RuParameterType paramType, DWORD param) = 0;
	virtual BOOL							Ru3D_SetTexture(IRuTexture *texture, RuTextureChannel texChannel = ruTEXCHANNEL_DIFFUSEMAP) = 0;
	virtual void							Ru3D_Line_Begin() = 0;
	virtual void							Ru3D_Line_End() = 0;
	virtual void							Ru3D_Line_Submit(const CRuVector3 &lineVertex) = 0;
	virtual void							Ru3D_Line_Submit(const CRuAABB &boundingBox) = 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:
	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_specularPosition;								//!< Object-space specular light source position
	};

	struct RenderTargetDescriptor
	{
		IRuObject*							m_renderTarget;									//!< Render target object
		IRuSurface*							m_depthStencilSurface;							//!< Depth stencil surface, NULL = use default
		INT32								m_priority;										//!< Render target priority
		DWORD								m_clearFlags;									//!< RT clearing flags
		RUCOLOR								m_clearColor;									//!< Color to clear to
		REAL								m_clearZ;										//!< Z to clear to
		DWORD								m_clearStencil;									//!< Stencil value to clear to
		BOOL								m_cleared;										//!< Has the render target been cleared?

		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 DynamicMapDescriptor
	{
		INT32								m_rtIndex;
		IRuTexture*							m_renderTarget;
		CRuCamera*							m_camera;

		RuTextureChannel					m_mapChannel;									//!< Channel this map applies to

		CRuPlane4							m_splitPlane;									//!< Splitting plane (if applicable)
	};

	struct RenderQueueEntry
	{
		// Render target and camera settings
		INT32								m_rtIndex;										//!< Render target to which this entry is rendered
		CRuCamera*							m_camera;										//!< Camera used to render this entry
		const CRuPlane4*					m_frustumOverride;								//!< Override for the frustum check, NULL to disable

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

		// Entity render information
		CRuEntity*							m_entity;										//!< Source entity for this queue entry
		CRuFrameHierarchy*					m_hierarchy;
		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
	};

	// Current scene state
	INT32									m_iterationNumber;								//!< Iteration number of the current scene
	INT32									m_curRTIndex;									//!< Index to the currently active RT
	UINT32									m_nextObjectID;

	// Core-level objects
	IRuInstanceManager*						m_instanceManager;								// Instance manager
	CRuCamera*								m_camera;										// Currently active camera

	CRuParameterBlock						m_paramBlock;

	CRuArrayList<DynamicMapDescriptor>		m_dynamicMaps;									//!< List of reflect/refract maps that need to be rendered
	IRuTexture*								m_nullTexture;									//!< NULL texture map
	IRuTexture*								m_cubicFilterLUT;								//!< Cubic filter look-up texture
	IRuSurface*								m_auxDepthStencilSurface;						//!< Auxiliary depth stencil surface

	// 2D toolkit objects
	REAL									m_ru2D_Viewport_Width;
	REAL									m_ru2D_Viewport_Height;

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

	INT32									m_ru2D_ActiveTextureHandle;

	Ru2D_EffectType							m_ru2D_EffectType;								//!< Currently selected 2D effect type
	Ru2D_RotateMode							m_ru2D_RotateMode;								//!< Current UV rotation mode
	BOOL									m_ru2D_LineMode;								//!< Is current submission mode in line mode?
	DWORD									m_ru2D_DiffuseColor;							//!< Currently used diffuse color
	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
	CRuDynamicMesh*							m_ru2D_Mesh;									//!< Renderer buffer mesh
	CRuMaterial*							m_ru2D_Material;								//!< Renderer buffer material
	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;

	// 3D toolkit objects
	BOOL									m_ru3D_LineMode;								// Current submission mode in line mode?
	INT32									m_ru3D_MaxPrimitiveCount;						// Maximum number of primitives in the buffer
	INT32									m_ru3D_PrimitiveCount;							// Number of primitives in the buffer
	CRuDynamicMesh*							m_ru3D_Mesh;									// Output mesh
	CRuMaterial*							m_ru3D_Material;								// Output material
	CRuArrayList<CRuVector3>				m_ru3D_VertexList;								// Piecewise submission vertex list

	// Scene parsing stacks and queues
	CRuStack<CRuEntity *>					m_entityStack;									// Stack used for traversing an entity tree
	CRuArrayList<RenderTargetDescriptor>	m_renderTargets;								// Render targets used in the current scene
	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;

	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

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

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

	CRuLRUQueue<UINT64, CachedSkin>*		m_skinningCache;

	// 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

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

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

	// System linkup
	virtual IRuInstanceManager*				GetInstanceManager();
	virtual BOOL							SetInstanceManager(IRuInstanceManager *instanceManager);

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

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

	// Manual camera setup
	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();

	IRuTexture*								GetTestShadowMap()
	{
		if(m_dynamicMaps.Count())
			return m_dynamicMaps[m_dynamicMaps.Count() - 1].m_renderTarget;

		return NULL;
	}

	// Scenegraph rendering
	virtual void							Scene_Begin();
	virtual void							Scene_SetRenderTarget(IRuObject *renderTarget, INT32 priority, DWORD clearFlags, RUCOLOR color, REAL z, DWORD stencil);
	virtual void							Scene_Submit(CRuEntity *sceneRoot, CRuCamera *camera = NULL);
	virtual void							Scene_Submit(CRuMatrix4x4 *worldTransform, IRuMesh *mesh, IRuMaterial *material);
	virtual void							Scene_Flush();
	virtual void							Scene_End();

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

	// Immediate rendering

	// 2D toolkit
	virtual BOOL							Ru2D_Begin();
	virtual BOOL							Ru2D_End();
	virtual BOOL							Ru2D_SetViewportScale(REAL width, REAL height);
	virtual IRuTexture*						Ru2D_GetReservedDXT5Texture();
	virtual BOOL							Ru2D_SetScissorRect(CRuRectangle *scissorRect);
	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, RuTextureChannel texChannel = ruTEXCHANNEL_DIFFUSEMAP);
	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_Fan_Begin(const CRuRectangle &srcRect, const CRuRectangle &destRect);
	virtual void							Ru2D_Fan_End();
	virtual void							Ru2D_Fan_Submit(const CRuVector3 &fanVertex);
	virtual void							Ru2D_Line_Begin();
	virtual void							Ru2D_Line_End();
	virtual void							Ru2D_Line_Submit(const CRuVector3 &lineVertex);
	virtual void							Ru2D_Line_Submit(const CRuVector3 &lineVertex0, const CRuVector3 &lineVertex1);
	virtual void							Ru2D_Line_Submit(INT32 numLines, const CRuVector3 *lineVertices, const DWORD *color);

	void									Ru2D_ConvertCoordinates(CRuRectangle &rectangle);

	// 3D toolkit
	virtual BOOL							Ru3D_Begin();
	virtual BOOL							Ru3D_End();
	virtual BOOL							Ru3D_SetShader(IRuShader *shader);
	virtual BOOL							Ru3D_SetShaderParameter(RuParameterType paramType, DWORD param);
	virtual BOOL							Ru3D_SetTexture(IRuTexture *texture, RuTextureChannel texChannel = ruTEXCHANNEL_DIFFUSEMAP);
//	virtual void							Ru3D_Render(IRuMesh *mesh, IRuMaterial *material);
	virtual void							Ru3D_Line_Begin();
	virtual void							Ru3D_Line_End();
	virtual void							Ru3D_Line_Submit(const CRuVector3 &lineVertex);
	virtual void							Ru3D_Line_Submit(const CRuAABB &boundingBox);

protected:
	CRuMatrix4x4*							CreateMatrix();

	CRuParameterCache*						CreateParameterCache();
	void									FlushParameterCacheStack();

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

	void									ResetDynamicMaps();

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

	void									Scene_QueueRenderEntry(RenderQueueEntry &rqEntry);
	void									Scene_ScanForDynamicTextures(RenderQueueEntry &rqEntry);

	void									Ru2D_RenderBatch();

	void									Ru3D_RenderBatch();
};

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

extern RUAPI CRuRendererOptions				g_ruRendererOptions;
extern RUAPI IRuSceneRenderer*				g_ruSceneRenderer;

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

#pragma managed

#endif
