/**
*
* \file Asura_PlatformHierarchy.cpp
*
* Created by Kevin Lea - 30/11/2001 14:57:53
*
* Copyright (C) 2001 Rebellion, All Rights Reserved.
*
* \brief
*
**/

#include "Core/Asura.h"

#ifdef ASURA_TOOLS
#include "Mesh/Asura_Mesh.h"
#endif

#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"

#include "Debugging/Asura_Assert.h"

#if defined (ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER
#include "Rendering/Asura_Direct3D_SmoothSkinRenderer.h"
#endif
#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER
#include "Rendering/Asura_PlatformRenderCharacter.h"
#endif
#include "Rendering/Asura_Platform_RenderObjectHierarchy.h"

#include "Camera/Asura_Camera.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Module.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Core/Asura_Material_System.h"

#include "Rendering/Asura_Direct3D_Caps.h"
#include "Rendering/Asura_Direct3D_UserOptions.h"
#include "Rendering/Asura_Direct3D_ShadowBuffer.h"

#include "Rendering/Asura_Direct3D_SkinRenderer.h"

//#include "Core/Asura_LOS.h"

#ifdef ASURA_USE_SHADOWDECALS
#include "SpecialFX/Asura_ShadowDecal_System.h"
#endif

#ifdef ASURA_TOOLS
#include "Misc/Tools_Render.h"
#endif

#if defined(ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER && (!defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER)
// Both renderers are available
// Set up a debugvar to let the user choose which one they're using
DEBUGVAR bool dbg_bUseSM3SmoothSkinRenderer = true;
#endif

const float fSHADOW_CULL_DISTANCE_SQD = 25.f * 25.f;

/// Kevin Lea - 26/02/2002 14:50:53 - version 2
/// each skin vertex only needs it's position in model
/// space; a little extra calculation is required
/// to change the bone matrices and positions into
/// the correct space.
void Asura_Hierarchy_System::Platform_RenderSkin( const Asura_Animation* pxAnim, 
                                                  float fAlpha, int iTexIDOverride,
                                                  u_int uCamVisFlags, u_int uRenderFlags,
                                                  const Asura_Vector_3& xBSCentre, float fBSRadius )											  
{ 

#if !(defined (ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) && ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER)
#if defined(ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER
	if(!dbg_bUseSM3SmoothSkinRenderer)
#endif
	{

		bool bModelSatisfiesBoneLimits = true;

		// KJFL - 2004-02-24 10:12:12 - the bone limits are dependent on a) hardware support and b) quality of hardware support
		// I plan to replace these renderers with a new system soon so there's always a hardware-friendly fallback
		//if (Asura_Direct3D_Caps::HasHardwareVertexShaders())
		//{
		//	// each bone requires 3 contants (each 'constant' is actually a 4-vector), and lighting/transform requires another 12
		//	const u_int uConstantsRequired = s_pxSkin->NumberOfBones*3 + 12;
		//	
		//	bModelSatisfiesBoneLimits = (uConstantsRequired <= Asura_Direct3D_Caps::GetNumberOfVertexShaderConstants());
		//}
		//else
		//{
		//	// KJFL - 2004-02-24 10:17:57 -  if no hardware support DirectX8 has 96 constants which equates to a 28 bone limit
		//	// (This may be different once we fully move the internals to DirectX9, which would make things easier.)
		//	bModelSatisfiesBoneLimits = (s_pxSkin->NumberOfBones<=28);
		//}


		if (bModelSatisfiesBoneLimits)
		{
			//Get the first bone position for the LOD distance test. 
			//(need to grab it now , since the positions are about to be messed with below)
			//RAR 01/09/2005
			Asura_Vector_3 xFirstBonePos = GetWorldspaceBonePos(0);
			//Get the centre position for lighting
			Asura_Vector_3 xObjectCentre = s_pxSkin->GetBB().CalculateCentre();
			//Displace the position by the amount it has moved from its rest pose position.
			//This should help with lighting animations that move  away from their compass point. RAR 02/07/03
#if defined(ASURA_HIERARCHY_QUAD)
			xObjectCentre += xFirstBonePos - s_pxSkin->m_pxBoneRestPoseTransforms[0].GetTranslation();
#else
			xObjectCentre += xFirstBonePos - s_pxSkin->BoneRestPosition[0];
#endif	// defined(ASURA_HIERARCHY_QUAD)


			/// Kevin Lea - 26/02/2002 15:06:58 - make bone transforms valid for model space
			Asura_Bounding_Box xBB;
			xBB.Reset();

			for (int b=0; b<s_pxSkin->NumberOfBones; b++)
			{
				xBB.Set(GetWorldspaceBonePos(b));

#if defined(ASURA_HIERARCHY_QUAD)
				const Asura_AMatrix4x4&	xBoneRestPoseInverseTransform	= s_pxSkin->m_pxBoneRestPoseInverseTransforms[b];
				const Asura_AMatrix4x4	xWorldspaceBoneTransform(GetWorldspaceBoneTransform(b));

				xWorldspaceBoneTransform.Apply(xBoneRestPoseInverseTransform, s_axBoneTransforms[b]);
#else
				s_axBoneOri[b] = GetWorldspaceBoneOri(b) * s_pxSkin->BoneRestOrientation[b];
				s_axBonePos[b] = GetWorldspaceBonePos(b) - s_pxSkin->BoneRestPosition[b]*s_axBoneOri[b];
#endif	// defined(ASURA_HIERARCHY_QUAD)
			}


			bool bShadowRequired = (Asura_Direct3D_UserOptions::SHADOW_PLANAR == Asura_Direct3D_UserOptions::GetSmoothSkinShadows())
								&& !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW);

#if defined(ASURA_HIERARCHY_QUAD)
			Asura_Vector_3 xShadowPosition = s_xBaseTransform.GetTranslation();
#else
			Asura_Vector_3 xShadowPosition = *s_pxPos;
#endif	// defined(ASURA_HIERARCHY_QUAD)

	#if !defined (ASURA_USE_SHADOWDECALS)
			if(bShadowRequired)
			{
				// Just check that we actually need a shadow - if we're too far away from all cameras, we needn't bother
				float fDistance = Asura_Camera::FindAdjustedMinDistSqdFromCam(xBSCentre, uCamVisFlags);
				bShadowRequired = (fDistance <= fSHADOW_CULL_DISTANCE_SQD);
				
				// If it's near enough to a camera, we still need a shadow
				if(bShadowRequired)
				{

					// If we can get a shadow buffer, we only need to render to it once
					Asura_Direct3D_ShadowBuffer* pxBuffer;
					pxBuffer = Asura_Direct3D_ShadowBufferSystem::GetShadowBufferForAnim(pxAnim, xBSCentre, fBSRadius);
					if(pxBuffer)
					{
						// get the detail skin for this distance
						const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance((xBSCentre - pxBuffer->GetLightPosition()).Magnitude());
		
						pxBuffer->BeginCollectingShadows();
						Asura_Direct3D_CharacterRenderer::Render(pxDetailSkin, pxAnim, xObjectCentre, fAlpha, true);
						pxBuffer->EndCollectingShadows();
		
						bShadowRequired = false;
					}
					else
					{
						// lift the shadow up off the floor ever so slightly (1cm) to relieve z-fighting problems
						xShadowPosition.y = pxAnim->GetGroundYPosition(&xShadowPosition) - 0.01f;
					}
				}
			}
	#endif
			

			//void (*pfnRenderFunction)(Asura_Hierarchy_Skin* pxSkin, const Asura_Animation* pxAnim, const Asura_Vector_3& xObjectCentre,const float fAlpha, bool bOnlyDepth);
		
			/*switch( Asura_Render::GetRenderStyle() )
			{
				default:
				
				case ASURA_RENDERSTYLE_NORMAL:
				{
					pfnRenderFunction = Asura_Direct3D_CharacterRenderer::Render;
					break;
				}

				case ASURA_RENDERSTYLE_GRAPHICNOVEL:
				{
					pfnRenderFunction = Asura_Direct3D_CharacterRenderer::RenderGraphicNovel;
					break;
				}
			}*/

			for(int i=0; i<Asura_Camera::GetNumberOfCameras(); i++)
			{
				if((1<<i) & uCamVisFlags)
				{
					if( !s_bJustRenderForCurCam )
					{
    					Asura_Camera::SetCameraIndex(i);
						Asura_Render::ApplyCameraSettings();
					}

					//Get the skin/mesh accoring to distance from this camera
					float fDistance = (*Asura_Camera::GetPosition() - xFirstBonePos).Magnitude();
					const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);

		
					// KJFL - 2004-02-23 14:49:00 - render out shadow if required
					if( bShadowRequired && !s_bJustRenderForCurCam )
					{
						// bShadowRequired has been turned off if we used a shadow buffer
						// Old-style shadows
	#if !defined (ASURA_USE_SHADOWDECALS)
						Asura_Direct3D_CharacterRenderer::RenderShadow( pxDetailSkin, xShadowPosition );
	#endif
					}

					Asura_Direct3D_CharacterRenderer::Render(pxDetailSkin, pxAnim, xObjectCentre, fAlpha, false, iTexIDOverride);			
				}

			}

			// shadow decals only need to be added once, not per camera
	#if defined (ASURA_USE_SHADOWDECALS)
			if (uCamVisFlags && bShadowRequired)
			{
				Asura_ShadowDecal_System::AddShadow( xBB, pxAnim );
			}
	#endif

			return;
		}

		// KJFL - 2004-02-23 14:52:05 - "Tools renderer" which is a fallback renderer for models over the bone limit
	#ifdef ASURA_TOOLS
		static Asura_Matrix_3x3 s_axBoneRelativeOrientation[100];
		Asura_Assert(s_pxSkin->NumberOfBones<100,"");

		//Need to get the orientation relative to the rest pose to calculate the normals correctly.
		//(However the non-relative orientations need to be preserved for calculating vertex positions)
		//RAR 15/01/03
#if defined(ASURA_HIERARCHY_QUAD)
		for (int b=0; b<s_pxSkin->NumberOfBones; b++)
		{
			Asura_AMatrix4x4	xTemp;

			s_axBoneTransforms[b].Apply(s_pxSkin->m_pxBoneRestPoseTransforms[b], xTemp);
			s_axBoneTransforms[b].ToMatrix(s_axBoneRelativeOrientation[b]);
		}
#else
		for (int b=0; b<s_pxSkin->NumberOfBones; b++)
		{
			s_axBoneRelativeOrientation[b] = s_axBoneOri[b] * s_pxSkin->BoneRestOrientation[b];
		}
#endif	// defined(ASURA_HIERARCHY_QUAD)

		//for keeping track of which detail levels have had their vertices calculated
		Asura_Mesh* apxMeshUpdated[ASURA_MAX_NO_OF_CAMERAS];
		int iNumMeshUpdated = 0;
		
		extern void RenderAnimationMesh_GraphicNovel(Asura_Mesh* mesh, const Asura_Vector_3& position);

#if defined(ASURA_HIERARCHY_QUAD)
		const Asura_AMatrix4x4*	pxRenderTransform	= &s_xBaseTransform;
		if(s_bBonesInWorldspace)
		{
			//world position/orientation already including in bone positions
			pxRenderTransform	= &Asura_Maths::IdentityAMatrix4x4;
		}
#else
		const Asura_Vector_3* pxRenderPos = s_pxPos;
		const Asura_Matrix_3x3* pxRenderOri = s_pxOri;
		if(s_bBonesInWorldspace)
		{
			//world position/orientation already including in bone positions
			pxRenderPos = &Asura_Maths::ZeroVector3;
			pxRenderOri = &Asura_Maths::IdentityMatrix3x3;
		}
#endif	// defined(ASURA_HIERARCHY_QUAD)
		
		for(int i=0; i<Asura_Camera::GetNumberOfCameras(); i++)
		{
			if((1<<i) & uCamVisFlags)
			{
				if( !s_bJustRenderForCurCam )
				{
    				Asura_Camera::SetCameraIndex(i);
					Asura_Render::ApplyCameraSettings();
				}
				
				//Get the skin/mesh accoring to distance from this camera
				float fDistance = (*Asura_Camera::GetPosition() - GetWorldspaceBonePos(0)).Magnitude();
				Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);
				Asura_Mesh* pxDetailMesh = pxDetailSkin->GetMesh();
				Asura_Assert(pxDetailMesh,"");

				//See if this detail level has been updated yet
				bool bUpdated = false;
				for(int j=0;j<iNumMeshUpdated;j++)
				{
					if(apxMeshUpdated[j] == pxDetailMesh)
					{
						bUpdated = true;
						break;
					}
				}
				if(!bUpdated)
				{
					int iMaxBonesPerVertex = Asura_Direct3D_CharacterRenderer::GetMaxBonesPerVertex();
					apxMeshUpdated[iNumMeshUpdated++] = pxDetailMesh;
					//Need to update the vertex positions and normals for this mesh
					#if 1
					for (int v=0; v<pxDetailSkin->NumberOfVertices; v++)
					{
						pxDetailMesh->VertexDataPtr[v].Position.Zero();

						float totalWeight=0;
						
						Asura_Assert(iMaxBonesPerVertex>=1 && iMaxBonesPerVertex<=4,"");
						for (int b=0; b<iMaxBonesPerVertex; b++)
						{
							float weight = pxDetailSkin->VertexData[v].Weight[b];
							if (weight==-1.0f) break;
							
							Asura_Assert(weight<=1.0f,"Invalid bone weighting");
							Asura_Assert(weight>=0.0f,"Invalid bone weighting");

							int bone = pxDetailSkin->VertexData[v].BoneID[b];

#if defined(ASURA_HIERARCHY_QUAD)
							const Asura_AVector4	xVertex(pxDetailSkin->VertexData[v].Offset[b], 1.0f);
							Asura_AVector4			xTransformedVertex;
							Asura_Vector_3			vertex;

							xTransformedVertex = s_axBoneTransforms[bone].Apply(xVertex);
							vertex.SetX(xTransformedVertex.GetX());
							vertex.SetY(xTransformedVertex.GetY());
							vertex.SetZ(xTransformedVertex.GetZ());
#else
							Asura_Matrix_3x3 m = s_axBoneOri[bone];
							Asura_Vector_3 p = s_axBonePos[bone];
							Asura_Vector_3 vertex = (pxDetailSkin->VertexData[v].Offset[b]*m) + p;
#endif	// defined(ASURA_HIERARCHY_QUAD)

							vertex*=weight;
							
							pxDetailMesh->VertexDataPtr[v].Position += vertex;
							totalWeight += weight;
						}
						//if not using all the bones , we will need to renomralise
						if(iMaxBonesPerVertex<4)
						{
							pxDetailMesh->VertexDataPtr[v].Position /= totalWeight;	
						}
						
#if defined(ASURA_HIERARCHY_QUAD)
						const Asura_AVector4	xVertex(pxDetailMesh->VertexDataPtr[v].Position, 1.0f);
						Asura_AVector4			xTransformedVertex;
						Asura_Vector_3			vertex;

						xTransformedVertex = pxRenderTransform->Apply(xVertex);
						pxDetailMesh->VertexDataPtr[v].Position.SetX(xTransformedVertex.GetX());
						pxDetailMesh->VertexDataPtr[v].Position.SetY(xTransformedVertex.GetY());
						pxDetailMesh->VertexDataPtr[v].Position.SetZ(xTransformedVertex.GetZ());
#else
						pxDetailMesh->VertexDataPtr[v].Position = pxDetailMesh->VertexDataPtr[v].Position * (*pxRenderOri);
#endif	// defined(ASURA_HIERARCHY_QUAD)
					}
					#endif	

					if (pxDetailSkin->NormalData==0)
					{
						pxDetailSkin->CalculateSkinNormalData();
					}
					#if 01
					for (int pID=0; pID<pxDetailMesh->NumberOfPolygons; pID++)
					{
						for (int v2=0; v2<4; v2++)
						{
							uint16 vID = pxDetailMesh->PolygonDataPtr[pID].VertexID[v2];
							if (vID!=0xffff)
							{
								int bone = pxDetailSkin->VertexData[vID].BoneID[0];
								Asura_Vector_3 vertex = (pxDetailSkin->NormalData[pID].VertexNormal[v2])*(s_axBoneRelativeOrientation[bone]);
								pxDetailMesh->NormalsDataPtr[pID].VertexNormal[v2]=vertex;
							}
						}
					}
					#endif
				}

				// KJL - 31/07/2003 10:03:05 - need to make const
#if defined(ASURA_HIERARCHY_QUAD)
				Asura_Vector_3	xRenderPosition = pxRenderTransform->GetTranslation();
				RenderAnimationMesh_GraphicNovel(pxDetailMesh, xRenderPosition);
#else
				RenderAnimationMesh_GraphicNovel(pxDetailMesh, *pxRenderPos);
#endif
			}

		}
	#endif
	}
#if defined(ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER
	else
#endif

#endif

#if defined(ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER
	{
		if( s_bBonesInWorldspace )
		{
#if defined(ASURA_HIERARCHY_QUAD)
			s_xInverseBaseTransform.ApplyToArray(s_axBoneTransforms, s_pxSkin->NumberOfBones, s_axBoneTransforms);
#else
			// If the bones are in world space, have to switch them back to model space to avoid skinning inaccuracies

			Asura_Matrix_3x3 xOriTranspose = *s_pxOri;
			xOriTranspose.Transpose();

			for( int i = 0; i < s_pxSkin->NumberOfBones; ++i )
			{
				s_axBoneOri[i] = xOriTranspose * s_axBoneOri[i];

				s_axBonePos[i] -= *s_pxPos;
				s_axBonePos[i] = s_axBonePos[i] * xOriTranspose;
			}
#endif
			s_bBonesInWorldspace = false;
		}

		for( int i = 0; i < s_pxSkin->NumberOfBones; ++i )
		{
#if defined(ASURA_HIERARCHY_QUAD)
			s_axBoneTransforms[i].Apply(s_pxSkin->m_pxBoneRestPoseInverseTransforms[i], s_axBoneTransforms[i]);
#else
			s_axBoneOri[i] = s_axBoneOri[i] * s_pxSkin->BoneRestOrientation[i];
			s_axBonePos[i] -= s_pxSkin->BoneRestPosition[i] * s_axBoneOri[i];
#endif
		}
		if (s_pxSkin != pxAnim->GetSkin())
		{
			// Select LOD mesh based on distance to first bone, since object may be animating far away from its location
			const float fDistance = (GetWorldspaceBonePos(0) - *Asura_Camera::GetPosition()).Magnitude();
			const Asura_Hierarchy_Skin* const pxSkinLOD = s_pxSkin->GetDetailSkinFromDistance( fDistance );


			if( ShouldJustRenderForCurCam() )
			{
#if defined(ASURA_HIERARCHY_QUAD)
				Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, s_xBaseTransform, s_xInverseBaseTransform, s_axBoneTransforms );
#else
				Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, *s_pxPos, *s_pxOri, s_axBonePos, s_axBoneOri );
#endif

			}
			else
			{
				for( int u = 0; u < Asura_Camera::GetNumberOfCameras(); ++u )
				{
					if( !(uCamVisFlags & (1 << u)) ) continue;

					Asura_Camera::SetCameraIndex(u);
					Asura_Render::ApplyCameraSettings();

#if defined(ASURA_HIERARCHY_QUAD)
					Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, s_xBaseTransform, s_xInverseBaseTransform, s_axBoneTransforms );
#else
					Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, *s_pxPos, *s_pxOri, s_axBonePos, s_axBoneOri );
#endif
				}
			}
		} 
		else 
		{
			for( int iSkinIndex = 0; iSkinIndex < ASURA_ANIMATION_SKIN_COUNT; ++iSkinIndex )
			{
				const Asura_Hierarchy_Skin *pxSkin = pxAnim->GetSkin(iSkinIndex);

				if (pxSkin)
				{
					// Select LOD mesh based on distance to first bone, since object may be animating far away from its location
					const float fDistance = (GetWorldspaceBonePos(0) - *Asura_Camera::GetPosition()).Magnitude();
					const Asura_Hierarchy_Skin* const pxSkinLOD = pxSkin->GetDetailSkinFromDistance( fDistance );


					if( ShouldJustRenderForCurCam() )
					{
#if defined(ASURA_HIERARCHY_QUAD)
						Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, s_xBaseTransform, s_xInverseBaseTransform, s_axBoneTransforms );
#else
						Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, *s_pxPos, *s_pxOri, s_axBonePos, s_axBoneOri );
#endif
					}
					else
					{
						for( int u = 0; u < Asura_Camera::GetNumberOfCameras(); ++u )
						{
							if( !(uCamVisFlags & (1 << u)) ) continue;

							Asura_Camera::SetCameraIndex(u);
							Asura_Render::ApplyCameraSettings();

#if defined(ASURA_HIERARCHY_QUAD)

							if( Asura_Direct3D_SkinRenderer::IsEnabled() )
							{
								Asura_Direct3D_SkinRenderer::RenderSkin( *pxAnim, *pxSkinLOD,
									s_xBaseTransform, s_axBoneTransforms );
							}
							else
							{
								Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD,
									s_xBaseTransform, s_xInverseBaseTransform, s_axBoneTransforms );
							}
#else
							Asura_Direct3D_SmoothSkinRenderer::Render( *pxAnim, *pxSkinLOD, *s_pxPos, *s_pxOri, s_axBonePos, s_axBoneOri );
#endif
						}
					}
				}
			}
		}

	}
#endif // ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER
}



void Asura_Hierarchy_System::Platform_RenderHierarchy(const Asura_Animation* pxAnim, u_int uCamVisFlags, u_int uRenderFlags, const Asura_Vector_3& xBSCentre, float fBSRadius, 
													  const int iTexIDOverride /*=ASURA_TEXTURE_NONE*/ )
{
	void (*pfnBeginRenderFn)(const Asura_Animation*, const Asura_Hierarchy_Skin*);
	void (*pfnRenderFn)(const Asura_Animation*, const Asura_Hierarchy_Skin*, const int, const Asura_Vector_3&, const Asura_Matrix_3x3&, bool);
#ifdef ASURA_TOOLS
	void (*pfnRenderOutlineFn)(const Asura_Animation*, const Asura_Hierarchy_Skin*, const int, const Asura_Vector_3&, const Asura_Matrix_3x3&);
#endif
	void (*pfnEndRenderFn)();

#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
	pfnBeginRenderFn = Asura_Direct3D_ObjectHierarchyRenderer::BeginRender_MaterialResponse;
	pfnRenderFn = Asura_Direct3D_ObjectHierarchyRenderer::Render_MaterialResponse;
	pfnEndRenderFn = Asura_Direct3D_ObjectHierarchyRenderer::EndRender_MaterialResponse;
	#ifdef ASURA_TOOLS
		pfnRenderOutlineFn = Asura_Direct3D_ObjectHierarchyRenderer::Render_Outline_EffectsSystem;
	#endif
#else // !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
	pfnBeginRenderFn = Asura_Direct3D_ObjectHierarchyRenderer::BeginRender;
	pfnRenderFn = Asura_Direct3D_ObjectHierarchyRenderer::Render;
	#ifdef ASURA_TOOLS
		pfnRenderOutlineFn = Asura_Direct3D_ObjectHierarchyRenderer::Render_Outline;
	#endif
	pfnEndRenderFn = Asura_Direct3D_ObjectHierarchyRenderer::EndRender;
#endif // !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM

	if( s_bJustRenderForCurCam )
	{
		pfnBeginRenderFn( pxAnim, s_pxSkin );

		#ifdef ASURA_TOOLS
		if(pxAnim->GetOutlineColour())
		{
			//Render selection outline
			for(int i=0;i<s_pxSkin->NumberOfBones;i++)
			{
				pfnRenderOutlineFn( pxAnim, s_pxSkin, i, GetWorldspaceBonePos(i), GetWorldspaceBoneOri(i) );
			}
		}
		#endif //ASURA_TOOLS

		for(int i=0;i<s_pxSkin->NumberOfBones;i++)
		{
			pfnRenderFn( pxAnim, s_pxSkin, i, GetWorldspaceBonePos(i), GetWorldspaceBoneOri(i), false );
		}
		pfnEndRenderFn();

		// KJFL - 2004-02-20 12:09:33 - assumed no shadow for 'JustRenderForCurCam', which is usually HUD stuff 
		// if change required, the shadow calls would go here.

		return;
	}

	bool bShadowRequired = (Asura_Direct3D_UserOptions::SHADOW_PLANAR == Asura_Direct3D_UserOptions::GetObjectHierarchyShadows())
			               && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW);

#if defined (ASURA_USE_SHADOWDECALS)
	bShadowRequired = false;
#endif

#if defined(ASURA_TARGET_SM3)
	bShadowRequired = false;
#endif

#if !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM

#if defined(ASURA_HIERARCHY_QUAD)
	Asura_Vector_3 xShadowPosition = s_xBaseTransform.GetTranslation();
#else
	Asura_Vector_3 xShadowPosition = *s_pxPos;
#endif	// defined(ASURA_HIERARCHY_QUAD)

	if(bShadowRequired)
	{
		float fDistance = Asura_Camera::FindAdjustedMinDistSqdFromCam(xBSCentre, uCamVisFlags);
		bShadowRequired = (fDistance <= fSHADOW_CULL_DISTANCE_SQD);

		if(bShadowRequired)
		{

			// try and get a shadow buffer
			Asura_Direct3D_ShadowBuffer* pxBuffer;
			pxBuffer = Asura_Direct3D_ShadowBufferSystem::GetShadowBufferForAnim(pxAnim, xBSCentre, fBSRadius);
			if(pxBuffer)
			{
				// get the detail skin for this distance
				const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance((xBSCentre - pxBuffer->GetLightPosition()).Magnitude());
				
				pxBuffer->BeginCollectingShadows();
				Asura_Direct3D_ObjectHierarchyRenderer::BeginRender( pxAnim, pxDetailSkin );
				for(int i=0;i<pxDetailSkin->NumberOfBones;i++)
				{
					Asura_Direct3D_ObjectHierarchyRenderer::Render(pxAnim, pxDetailSkin, i, GetWorldspaceBonePos(i), GetWorldspaceBoneOri(i) );
				}
				Asura_Direct3D_ObjectHierarchyRenderer::EndRender();
				pxBuffer->EndCollectingShadows();
				bShadowRequired = false;
			}
			else
			{
				// lift the shadow up off the floor ever so slightly (1cm) to relieve z-fighting problems
				xShadowPosition.y = pxAnim->GetGroundYPosition(&xShadowPosition) - 0.01f;
			}
		}
	}

#endif // !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM

	for(int i=0; i<Asura_Camera::GetNumberOfCameras(); i++)
	{
		if((1<<i) & uCamVisFlags)
		{
			Asura_Camera::SetCameraIndex(i);
		    Asura_Render::ApplyCameraSettings();
            
			//Get the skin/mesh accoring to distance from this camera
			float fDistance = (*Asura_Camera::GetPosition() - GetWorldspaceBonePos(0)).Magnitude();
			const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);

			#if defined ASURA_TARGET_SM3
			if( Asura_Direct3D_SkinRenderer::IsEnabled() )
			{
				#if defined ASURA_TOOLS

					if( pxAnim->GetOutlineColour() )
					{
						// Some hierarchies are used as collision meshes only, with no visible mesh.
						// In that case the standard render function won't draw any selection
						// outline. So instead of doing it inside the skin renderer as for smooth
						// skins we'll pull it out here and render using the original Asura_Mesh.
						// See cl #167611 for the original change

						for( int j = 0; j < pxDetailSkin->NumberOfBones; ++j )
						{
							Asura_Mesh* const pxMesh = pxDetailSkin->SectionData[j].Mesh;
							if( !pxMesh ) continue;

							const Asura_Vector_3 xPos = GetWorldspaceBonePos(j);
							const Asura_Matrix_3x3 xOri = GetWorldspaceBoneOri(j);
							const u_int uColour = pxAnim->GetOutlineColour();

							Tools_Render::RenderMeshOutline( pxMesh, &xPos, &xOri, uColour, true );
						}
					}

				#endif // defined ASURA_TOOLS

				Asura_Direct3D_SkinRenderer::RenderSkin( *pxAnim, *pxDetailSkin, s_xBaseTransform,
					s_axBoneTransforms );
			}
			else
			#endif
			{
				#ifdef ASURA_TOOLS
				if(pxAnim->GetOutlineColour())
				{
					// KJFL - 2004-02-23 14:45:00 - draw the hierarchy			
					pfnBeginRenderFn( pxAnim, pxDetailSkin );
					//Render selection outline
					for(int i=0;i<pxDetailSkin->NumberOfBones;i++)
					{
						pfnRenderOutlineFn( pxAnim, pxDetailSkin, i, GetWorldspaceBonePos(i), GetWorldspaceBoneOri(i) );
					}
				}
				#endif //ASURA_TOOLS
				// KJFL - 2004-02-23 14:45:00 - draw the hierarchy			
				pfnBeginRenderFn( pxAnim, pxDetailSkin );
				for(int i=0;i<pxDetailSkin->NumberOfBones;i++)
				{
					pfnRenderFn(pxAnim, pxDetailSkin, i, GetWorldspaceBonePos(i), GetWorldspaceBoneOri(i), false );
				}
				pfnEndRenderFn();
				
#if !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM

				// KJFL - 2004-02-23 14:45:15 - draw the hierarchy's shadow if required
				if(bShadowRequired)
				{
					Asura_Direct3D_ObjectHierarchyRenderer::BeginShadowRender( pxDetailSkin, xShadowPosition );
					for(int i=0;i<pxDetailSkin->NumberOfBones;i++)
					{
						Asura_Direct3D_ObjectHierarchyRenderer::RenderShadow( i, GetWorldspaceBonePos(i), GetWorldspaceBoneOri(i) );
					}
					Asura_Direct3D_ObjectHierarchyRenderer::EndShadowRender();
				}

#endif // !defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
			}
		}

	}
}

void Asura_Hierarchy_System::Platform_RenderSkinShadow( const Asura_Animation& xAnim, const Asura_Shadow& xShadow, const u_int uCamVisFlags, const u_int uRenderFlags )
{
#if defined(ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER

#if defined(ASURA_HIERARCHY_QUAD)
	const float fDistance = (GetWorldspaceBonePos(0) - *Asura_Camera::GetPosition()).Magnitude();
#ifdef ASURA_USE_LOWER_SKIN_LOD_FOR_SHADOWS
	// Drop LOD an extra level for shadow
	const Asura_Hierarchy_Skin* const pxSkinLOD = s_pxSkin->GetDetailSkinFromDistance( fDistance )->GetRelativeDetailSkin(1);
#else
	const Asura_Hierarchy_Skin* const pxSkinLOD = s_pxSkin->GetDetailSkinFromDistance( fDistance );
#endif

	// Only update the bones once since all skins need the same bones...
	if(xAnim.GetSkin() == s_pxSkin)
	{
		if( s_bBonesInWorldspace )
		{
			// If the bones are in world space, have to switch them back to model space to avoid skinning inaccuracies
			s_xInverseBaseTransform.ApplyToArray(s_axBoneTransforms, s_pxSkin->NumberOfBones, s_axBoneTransforms);
			s_bBonesInWorldspace = false;
		}

		for( int i = 0; i < s_pxSkin->NumberOfBones; ++i )
		{
			s_axBoneTransforms[i].Apply(s_pxSkin->m_pxBoneRestPoseInverseTransforms[i], s_axBoneTransforms[i]);
		}
	}

	if( Asura_Direct3D_SkinRenderer::IsEnabled() )
	{
		Asura_Direct3D_SkinRenderer::RenderSkin( xAnim, *pxSkinLOD, s_xBaseTransform,
			s_axBoneTransforms, &xShadow );
	}
	else
	{
		Asura_Direct3D_SmoothSkinRenderer::RenderShadow( xAnim, *pxSkinLOD, xShadow,
			s_xBaseTransform, s_axBoneTransforms );
	}

#else
	// TODO: LOD?
	// Only update the bones once since all skins need the same bones...
	if(xAnim.GetSkin() == s_pxSkin)
	{
		if( s_bBonesInWorldspace )
		{
			// If the bones are in world space, have to switch them back to model space to avoid skinning inaccuracies

			Asura_Matrix_3x3 xOriTranspose = *s_pxOri;
			xOriTranspose.Transpose();

			for( int i = 0; i < s_pxSkin->NumberOfBones; ++i )
			{
				s_axBoneOri[i] = xOriTranspose * s_axBoneOri[i];

				s_axBonePos[i] -= *s_pxPos;
				s_axBonePos[i] = s_axBonePos[i] * xOriTranspose;
			}
			s_bBonesInWorldspace = false;
		}

		for( int i = 0; i < s_pxSkin->NumberOfBones; ++i )
		{
			s_axBoneOri[i] = s_axBoneOri[i] * s_pxSkin->BoneRestOrientation[i];
			s_axBonePos[i] -= s_pxSkin->BoneRestPosition[i] * s_axBoneOri[i];
		}
	}

	Asura_Direct3D_SmoothSkinRenderer::RenderShadow( xAnim, *s_pxSkin, xShadow, *s_pxPos, *s_pxOri, s_axBonePos, s_axBoneOri );
#endif	// defined(ASURA_HIERARCHY_QUAD)

#endif
}

void Asura_Hierarchy_System::Platform_RenderHierarchyShadow( const Asura_Animation& xAnim, const Asura_Shadow& xShadow, const u_int uCamVisFlags, const u_int uRenderFlags )
{
#if defined(ASURA_TARGET_SM3)
	const float					fDistance	= (GetWorldspaceBonePos(0) - *Asura_Camera::GetPosition()).Magnitude();
	// (SB - 01/05/08) Support LOD skins for shadows
#ifdef ASURA_USE_LOWER_SKIN_LOD_FOR_SHADOWS
	// Drop LOD an extra level for shadow
	const Asura_Hierarchy_Skin*	pxSkinLOD	= s_pxSkin->GetDetailSkinFromDistance( fDistance )->GetRelativeDetailSkin(1);
#else
	const Asura_Hierarchy_Skin*	pxSkinLOD	= s_pxSkin->GetDetailSkinFromDistance( fDistance );
#endif

	if( Asura_Direct3D_SkinRenderer::IsEnabled() )
	{
		Asura_Direct3D_SkinRenderer::RenderSkin( xAnim, *pxSkinLOD, s_xBaseTransform,
			s_axBoneTransforms, &xShadow );
	}
	else
	{
		Asura_Direct3D_ObjectHierarchyRenderer::RenderShadow_EffectsSystem( xAnim, *pxSkinLOD,
			xShadow, fDistance );
	}
#endif
}

// RJF - 16 June 2005 - This function no longer makes sense because skins support multiple materials
/*static inline int GetMaterialIndexForSkin( const Asura_Hierarchy_Skin* pxSkin )
{
	if( pxSkin->IsSkinnedModel() )
	{
		return Asura_Direct3D_CharacterRenderer::GetMaterialIndex( pxSkin );
	}
	else
	{
		return Asura_Direct3D_ObjectHierarchyRenderer::GetMaterialIndex( pxSkin );
    }
}*/

int Asura_Hierarchy_Skin::Platform_GetTextureIndex() const
{
	//return Asura_Material_System::GetTextureID(GetMaterialIndexForSkin(this));	
	return ASURA_TEXTURE_NONE;
}

bool Asura_Hierarchy_Skin::Platform_IsOpaque( void ) const
{
	#if defined ASURA_TARGET_SM3
	if( Asura_Direct3D_SkinRenderer::IsEnabled() )
	{
		const Asura_Hierarchy_Skin* pxHierarchy = this;

		while( pxHierarchy )
		{
			const Asura_Direct3D_Skin* const pxSkin =
				Asura_Direct3D_SkinRenderer::GetSkin( pxHierarchy->GetHashID() );

			if( pxSkin && pxSkin->IsAnyMaterialTransparent() ) return false;

			pxHierarchy = pxHierarchy->m_pxNextLowerSkin;
		}

		return true;
	}
	else
	#endif
	{
		if(this->IsSkinnedModel())
		{
			bool bResult = true;	// If we can't get perform a test for any reason, assume we're opaque.
	#if defined(ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER) && ASURA_PC_USE_SHADER_MODEL_3_SMOOTH_SKIN_RENDERER
		#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER
				if(dbg_bUseSM3SmoothSkinRenderer)
		#endif
			{
				const Asura_Hierarchy_Skin*	pxHierarchySkin	= this;

				while(pxHierarchySkin != NULL)
				{
					const Asura_Direct3D_SmoothSkin*	pxSkin	= Asura_Direct3D_SmoothSkinRenderer::GetSmoothSkin(pxHierarchySkin->m_uNameHashID);

					if((pxSkin != NULL) && pxSkin->IsAnyMaterialTransparent())
					{
						// If any skin has transparent materials assume that all do - since 
						// we don't know which LOD we're actually going to render...
						return(false);
					}

					pxHierarchySkin	= pxHierarchySkin->m_pxNextLowerSkin;
				}
			}
		#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER
				else
		#endif
	#endif
	#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER
			{
				bResult = !Asura_Direct3D_CharacterRenderer::IsAnyMaterialTransparent( this );
			}
	#endif
			return bResult;
		}
		else
		{
			return !Asura_Direct3D_ObjectHierarchyRenderer::IsAnyMaterialTransparent(this);
		}
	}
}

void Asura_Hierarchy_System::Platform_Initialise( void )
{
}

void Asura_Hierarchy_System::Platform_Reset( void )
{
}

void Asura_Hierarchy_System::Platform_Shutdown( void )
{
}


/// End of file. ///
