//
// Asura_PlatformRenderCharacter.cpp
//
// Created by KJL - 14/04/2003 12:45:47
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#if !defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER

// This file's header.
#include "Rendering/Asura_PlatformRenderCharacter.h"

// Asura headers
#include "Animation/Asura_Hierarchy.h"
#include "Animation/Asura_Animation.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_TextureManagement.h"
#include "Core/Asura_Material_System.h"
#include "Core/Asura_NameTree.h"
#include "Core/Asura_Stream.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Assert.h"
#include "Light/Asura_GI_Solution.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Rendering/Asura_Direct3D_Caps.h"
#include "Rendering/Asura_Fog.h"
#include "Rendering/Asura_Render.h"

#ifdef ASURA_TOOLS
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "ChunkSystem/Asura_Chunk_ResourceFile_PC_Character.h"
#include "Mesh/Asura_Mesh.h"
#include "MeshProcessing/Asura_TriStripper.h"
#include "MeshProcessing/Tools_CharacterProcessor.h"
#include "MeshProcessing/Tools_CharacterProcessor_NormalMapped.h"
#include "MeshProcessing/Tools_SmoothSkin_Processor.h"
#include "MeshProcessing/Tools_SmoothSkin_Processor_PC.h"
#include "MeshProcessing/Tools_VIPM.h"
#endif

Asura_Direct3D_CharacterRenderer::RenderFunction *Asura_Direct3D_CharacterRenderer::s_pfnRenderFunction;

// KJL - 13/04/2003 15:03:29 - new renderer based on exported strips
// currently using a name tree 

#define ASURA_MAX_NO_OF_CHARACTER_STRIPS (32*3) //Increased the size a bit to allow for a couple of LOD models per mesh. RAR 05/09/02

// (SB - 20/11/06) Function pointers to set and revert pixel shader
Asura_Direct3D_CharacterRenderer::SetupPixelShader* Asura_Direct3D_CharacterRenderer::s_pfnSetupPixelShader = NULL;
Asura_Direct3D_CharacterRenderer::RevertPixelShader* Asura_Direct3D_CharacterRenderer::s_pfnRevertPixelShader = NULL;

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Direct3D_CharacterRender_Handler
//
////////////////////////////////////////////////////////////////////////////////

void Asura_Direct3D_CharacterRender_Handler::Reset (void)
{
	m_xHashTree.Reset();
}

const Asura_Direct3D_Character_Strip* Asura_Direct3D_CharacterRender_Handler::GetCharacterStrip(const Asura_Hierarchy_Skin* pxSkin)
{
	// KJL - 14/04/2003 14:38:52 - get strip for given name; asserts and returns 0 if name not found

	Asura_Assert(pxSkin,"Invalid skin pointer");
	if (!pxSkin) return 0;

	const Asura_Hash_ID						uHash	= pxSkin->m_uNameHashID;
	const Asura_Direct3D_Character_Strip*	pxStrip	= m_xHashTree.GetDataByHash(uHash);

	if (pxStrip == NULL)
	{
		// KJL - 14/04/2003 12:58:46 - character wasn't found i.e. no character strip of that name has
		// been loaded in. Now, if this is a tools build there's a chance that there'll be an Asura_Mesh 
		// and the associated data around to make a strip ourselves. If this isn't a tools build just
		// return NULL and the character won't be drawn (assuming all the functions that call this handle
		// the case gracefully) - we'll assert so people know there's a problem.

#ifdef ASURA_TOOLS
		{
			pxStrip = InitialiseSkinFromRawData(pxSkin);
			Asura_Assertf(pxStrip, ("A character skin that wasn't loaded as part of an export\ncouldn't be made from existing files either. Skin name: <%s>",pxSkin->Name.GetString()));

			return pxStrip;
		}
#else
		{
			Asura_Assertf(false, ("No strip data for character: <%s>", pxSkin->Name.GetString()) );
			return 0;
		}
#endif
	}

	return pxStrip;
}

void Asura_Direct3D_CharacterRender_Handler::ResetAllLODs(const Asura_Hierarchy_Skin* pxSkin)
{
	while(pxSkin)
	{
		m_xHashTree.DeleteNodeByHash(pxSkin->m_uNameHashID);
		pxSkin = pxSkin->m_pxNextLowerSkin;
	}
}

int Asura_Direct3D_CharacterRender_Handler::GetMaterialIndex(const Asura_Hierarchy_Skin* pxSkin) const
{
	const Asura_Direct3D_Character_Strip* pxStrip = m_xHashTree.GetDataByHash(pxSkin->m_uNameHashID);
	if(pxStrip)
	{
		return pxStrip->m_iMaterialIndex;	
	}
	return ASURA_MATERIAL_NONE;
}

bool Asura_Direct3D_CharacterRender_Handler::IsAnyMaterialTransparent(const Asura_Hierarchy_Skin* pxSkin) const
{
	const Asura_Direct3D_Character_Strip* pxStrip = m_xHashTree.GetDataByHash(pxSkin->m_uNameHashID);
	while(pxStrip)
	{
		if( Asura_Material_System::IsMaterialTransparent( pxStrip->m_iMaterialIndex )
		 || Asura_Material_System::IsMaterialPS2AdditiveAlpha( pxStrip->m_iMaterialIndex ) )
		{
			return true;
		}

		pxStrip = pxStrip->m_pxNextStrip;
	}
	return false;
}

Asura_Direct3D_Character_Strip* Asura_Direct3D_CharacterRender_Handler::AddCharacterStrip( const char* szName )
{ 
	bool bDidExist = true;
	Asura_Direct3D_Character_Strip* pxCharacter = m_xHashTree.AddOrFindNode(Asura_GetHashID(szName),&bDidExist,true); 

	Asura_Assert(pxCharacter,"Out of memory creating new hash tree node");
	Asura_Assertf(!(pxCharacter && bDidExist),("Duplicate item %s",szName));

	return (bDidExist) ? NULL : pxCharacter;
}

#ifdef ASURA_TOOLS

// KJL - 14/04/2003 14:42:41 - preprocessor fn required for Tools builds - I'll probably tidy this into a tools file if it's appropriate
const Asura_Direct3D_Character_Strip* Asura_Direct3D_CharacterRender_Handler::InitialiseSkinFromRawData(const Asura_Hierarchy_Skin* pxConstSkin)
{
	// Get a non-const copy of the skin to operate on
	// Need to get the top-most skin for this , since the LOD's aren't accessible via GetSkinByHash
	int iLODLevel = 0;
	const Asura_Hierarchy_Skin* pxConstParentSkin = pxConstSkin;
	while(pxConstParentSkin->m_pxNextHigherSkin)
	{
		pxConstParentSkin = pxConstParentSkin->m_pxNextHigherSkin;
		iLODLevel++;
	}
	Asura_Hierarchy_Skin* pxSkin = Asura_Animation_System::GetSkinByHash(pxConstParentSkin->m_uNameHashID);
	if(!pxSkin) return 0;
	//skip back down to the correct LOD
	for(int i=0;i<iLODLevel;i++)
	{
		if(!pxSkin->m_pxNextLowerSkin) return 0;
		pxSkin = pxSkin->m_pxNextLowerSkin;
	}

	Tools_SmoothSkin_Processor xProcessor;
	xProcessor.SetIncludeNormalMapData( false );
	if( !xProcessor.Initialise( pxSkin ) ) return 0;

	Tools_SmoothSkin_PC_Processor xPCProcessor(xProcessor);
	if( !xPCProcessor.Initialise(false) ) return 0;

	bool bConvertMaterialIDs = false;
	// use dummy stream to get the memory size required.
	Asura_Chunk_Dummy_Stream xDummyStream;
	if( !xPCProcessor.OutputToChunk(xDummyStream, bConvertMaterialIDs) ) return 0;

	u_int uSize = xDummyStream.GetPosition();
	void* pBuffer = Asura_Allocate(uSize);
	if (!pBuffer) return 0;
	
	// write chunk data to memory buffer
	Asura_Chunk_Memory_Stream xMemoryStream( uSize, pBuffer );
	if (!xPCProcessor.OutputToChunk(xMemoryStream, bConvertMaterialIDs))
	{
		Asura_Deallocate(pBuffer);
		return 0;
	}

	// restart stream
	xMemoryStream.SetPosition(0);

	Asura_Chunk_ResourceFile_PC_Character xChunk;
	xMemoryStream.Read( sizeof(xChunk), &xChunk );
	bool bWarnIfMeshExists = false;// if creating skin data for the loaded mesh in tools we want to skip this warning.
	xMemoryStream.SkipString();	// skip resource name (mesh name is after).
	const Asura_Direct3D_Character_Strip* pxStrip = 
		xChunk.ProcessFromChunkStream( xMemoryStream, bWarnIfMeshExists, bConvertMaterialIDs );

	Asura_Deallocate(pBuffer);

	return pxStrip;
}

#endif//ASURA_TOOLS


////////////////////////////////////////////////////////////////////////////////
//
// Asura_Direct3D_CharacterRenderer
//
////////////////////////////////////////////////////////////////////////////////

#if defined(ASURA_USE_MULTIPLE_HANDLERS)
Asura_Direct3D_CharacterRender_Handler*							Asura_Direct3D_CharacterRenderer::s_pxCurrentHandler	= NULL;		// Currently active handler.
Asura_TemplateHashTree<Asura_Direct3D_CharacterRender_Handler>	Asura_Direct3D_CharacterRenderer::s_xHandlerTree;					// Tree of all of the handlers available.
#else
Asura_Direct3D_CharacterRender_Handler							Asura_Direct3D_CharacterRenderer::s_xHandler;						// Single static-instance of a zone.
#endif

#ifdef ASURA_TOOLS
int	Asura_Direct3D_CharacterRenderer::s_iMaxBonesPerVertex = 4;
#endif//ASURA_TOOLS

void Asura_Direct3D_CharacterRenderer::Initialise(void)
{
}

void Asura_Direct3D_CharacterRenderer::Reset(void)
{
#if defined(ASURA_USE_MULTIPLE_HANDLERS)
	GetCurrentHandler().Reset();
#else
	Shutdown();
	Initialise();
#endif
}

void Asura_Direct3D_CharacterRenderer::Shutdown(void)
{
#if defined(ASURA_USE_MULTIPLE_HANDLERS)
	s_xHandlerTree.Reset();
	s_pxCurrentHandler	= NULL;
#else
	s_xHandler.Reset();
#endif	//defined(ASURA_USE_MULTIPLE_HANDLERS)
}

#if defined(ASURA_USE_MULTIPLE_HANDLERS)

void Asura_Direct3D_CharacterRenderer::ReparentHandler(Asura_Direct3D_CharacterRender_Handler& xSourceHandler, const Asura_Hash_ID uNewParentHash)
{
	Asura_Direct3D_CharacterRender_Handler*	pxParentHandler	= s_xHandlerTree.GetDataByHash(uNewParentHash);

	if(pxParentHandler != NULL)
	{
		xSourceHandler.m_xHashTree.SetParent(&(pxParentHandler->m_xHashTree));
	}
}

void Asura_Direct3D_CharacterRenderer::ReparentHandler(const Asura_Hash_ID uSourceHash, const Asura_Hash_ID uNewParentHash)
{
	Asura_Direct3D_CharacterRender_Handler*	pxHandler	= s_xHandlerTree.GetDataByHash(uSourceHash);

	if(pxHandler != NULL)
	{
		ReparentHandler(*pxHandler, uNewParentHash);
	}
}

Asura_Direct3D_CharacterRender_Handler* Asura_Direct3D_CharacterRenderer::CreateHandler (const Asura_Hash_ID uHash, const Asura_Hash_ID uParentHandlerHash)
{
	Asura_Direct3D_CharacterRender_Handler*	pxHandler	= s_xHandlerTree.GetDataByHash(uHash);

	// Do we already have this zone?
	if(pxHandler == NULL)
	{
		// No - create one...
		pxHandler	= s_xHandlerTree.AddNode(uHash);

		if(pxHandler != NULL)
		{
			ReparentHandler(*pxHandler, uParentHandlerHash);
		}
	}
	else
	{
		// Yes - this shouldn't happen...
		Asura_Assert(false, "Already have a character render-handler with this hash-ID.");
	}

	// Return the zone...
	return(pxHandler);
}

bool Asura_Direct3D_CharacterRenderer::DestroyHandler (const Asura_Hash_ID uHash)
{
	// Is this the current zone?
	if(s_pxCurrentHandler == s_xHandlerTree.GetDataByHash(uHash))
	{
		s_pxCurrentHandler	= NULL;
	}

	// Call the delete function...
	return(s_xHandlerTree.DeleteNodeByHash(uHash));
}

void Asura_Direct3D_CharacterRenderer::WriteHandlerReport(u_int uHandler, Asura_Stream& xLog)
{
	const u_int uLOGBUFFERSIZE = 64;
	char szLogBuffer[uLOGBUFFERSIZE];

	Asura_Direct3D_CharacterRender_Handler* pxHandler = s_xHandlerTree.GetDataByHash(uHandler);
	if (pxHandler)
	{
		if (!pxHandler->m_xHashTree.GetNumberOfNodes())
		{
			xLog << "PC Character:\t\t\tNone\n";
		}
		else
		{
			Asura_PrintfToBfr( szLogBuffer, "PSP Character:\t\t\t%d Item(s)\n", uLOGBUFFERSIZE, true, pxHandler->m_xHashTree.GetNumberOfNodes() );
			xLog << szLogBuffer;
		}
	}
	else
	{
		xLog << "PC Character:\t\t\tNo Handler\n";
	}
}

#endif	//#if defined(ASURA_USE_MULTIPLE_HANDLERS)

void Asura_Direct3D_CharacterRenderer::Render(const Asura_Hierarchy_Skin* pxSkin,const Asura_Animation* pxAnim, const Asura_Vector_3& xObjectCentre, const float fAlpha, bool bOnlyDepth /* = false */, int iTexIDOverride /*=ASURA_TEXTURE_NONE*/)
{
	Asura_Direct3D_CharacterRender_Handler&	xHandler	= GetCurrentHandler();
	const Asura_Direct3D_Character_Strip*	pxCharacter	= xHandler.GetCharacterStrip(pxSkin);

	// KJL - 13/04/2003 15:24:24 - check to see if we have a character strip for this model
	if(!pxCharacter) return;

	D3DPERFCALL(BeginEvent(0xff845fb4, L"Character Render"));

	while( pxCharacter )
	{
		// Choose renderer based on strip type
		switch(pxCharacter->m_eStripType)
		{
			case ASURA_D3D_CHARACTER_STRIP_BASIC: 
#ifdef ASURA_TOOLS
				if(pxAnim->GetOutlineColour() && !bOnlyDepth)
				{
					RenderBasic_Outline(pxCharacter, xObjectCentre, pxAnim->GetOutlineColour()); 
				}
#endif //ASURA_TOOLS
				if (s_pfnRenderFunction)
				{
					s_pfnRenderFunction(pxCharacter, pxAnim, fAlpha, xObjectCentre, bOnlyDepth, iTexIDOverride); 
				}
				else
				{
					RenderBasic(pxCharacter, pxAnim, fAlpha, xObjectCentre, bOnlyDepth, iTexIDOverride); 
				}
				break;
			default: Asura_Assert(0, "Unknown strip type"); break;
		}
		Asura_Render::AddToTrianglesRendered(pxCharacter->m_uNumberOfTriangles);

		// Move onto the next strip in this character, or NULL if it doesn't have one
		pxCharacter = pxCharacter->m_pxNextStrip;
	}

	D3DPERFCALL(EndEvent());
}

void Asura_Direct3D_CharacterRenderer::RenderBasic(const Asura_Direct3D_Character_Strip* pxCharacter, const class Asura_Animation* pxAnim, const float fAlpha, const Asura_Vector_3& xObjectCentre, bool bOnlyDepth /* = false */, int iTexIDOverride /*=ASURA_TEXTURE_NONE*/)
{
	D3DPERFCALL(BeginEvent(0xffa0573b, L"Character RenderBasic"));
#ifdef ASURA_TOOLS
	switch(s_iMaxBonesPerVertex)
	{
		case 1 :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_1BONE);
			break;
		case 2 :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_2BONES);
			break;
		case 3 :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_3BONES);
			break;
		default :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES);
			break;
	}
#else //ASURA_TOOLS
	if (	Asura_Direct3D_Caps::GetHardwareVertexShaderVersion() > 1.1f ||
			pxCharacter->m_ucRenderBoneCount > 28)
	{
		Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES);
	}
	else
	{
		Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES_VS1);
	}
#endif//ASURA_TOOLS

	Asura_Render::CurrentStates.RequestZBufferCompareMode( ASURA_ZBUFFERCMP_LESSEQUAL );

	const bool bShouldRenderTransparent = (pxAnim->GetAlpha() < 1.f) || Asura_Material_System::IsMaterialTransparent(pxCharacter->m_iMaterialIndex);

	if(!bOnlyDepth)
	{
		Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);

		Asura_Render::CurrentStates.RequestZBufferEnabled( true);
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(pxAnim->GetAlpha() > 0.5f);

		const int iTex = (iTexIDOverride == ASURA_TEXTURE_NONE ? Asura_Material_System::GetTextureID(pxCharacter->m_iMaterialIndex) : iTexIDOverride);
		Asura_Render::CurrentStates.RequestTextureIndex(0, iTex );

		Asura_Render::CurrentStates.RequestTranslucencyMode(bShouldRenderTransparent? ASURA_TRANSLUCENCY_NORMAL : ASURA_TRANSLUCENCY_OFF);
		Asura_Render::CurrentStates.RequestAlphaTest(Asura_Material_System::IsMaterialTransparent(pxCharacter->m_iMaterialIndex));

		if(Asura_Material_System::IsMaterialSelfIlluminating(pxCharacter->m_iMaterialIndex))
		{
			Asura_Render::CurrentStates.RequestTextureIndex(1, iTex);
			Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SELFILLUMINATE);
			if(!Asura_Material_System::IsMaterialTransparent(pxCharacter->m_iMaterialIndex))
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(1,D3DTSS_ALPHAOP,  D3DTOP_SELECTARG2 );
			}
		}
		else if(Asura_Material_System::IsMaterialSpecularSharp(pxCharacter->m_iMaterialIndex) 
			 || Asura_Material_System::IsMaterialSpecularSoft(pxCharacter->m_iMaterialIndex))
		{
			if (	Asura_Direct3D_Caps::GetHardwareVertexShaderVersion() > 1.1f ||
					pxCharacter->m_ucRenderBoneCount > 28)
			{
				Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES_SPECULAR);
			}
			else
			{
				Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES_SPECULAR_VS1);
			}
			Asura_Direct3D_Shaders::RequestPixelShader(ASURA_D3D_PS_SPECULAR_SOFT);
			Asura_Direct3D::D3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
		}
		else
		{
			Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS_2X);
			if(!Asura_Material_System::IsMaterialTransparent(pxCharacter->m_iMaterialIndex))
			{
				Asura_Direct3D::D3DDevice->SetTextureStageState(0,D3DTSS_ALPHAOP,  D3DTOP_SELECTARG2 );
			}
		}

		if(Asura_Fog::IsFogRenderable())Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);

		//texture anim stuff
		SetTextureAnimVertexShaderConstants(pxAnim,pxCharacter->m_iMaterialIndex);
	}
	else
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_JUSTSETZ);
		Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS_2X);
		Asura_Render::CurrentStates.RequestAlphaTest(Asura_Material_System::IsMaterialTransparent(pxCharacter->m_iMaterialIndex));
	}
	
	SetViewVertexShaderConstants();		
	SetLightVertexShaderConstants(fAlpha, true, GetFogValue(xObjectCentre), false, pxCharacter->m_iMaterialIndex);
//DeadCode VGP 20/03/2006 	SetBoneVertexShaderConstants();	

	if(pxCharacter->m_pxVertexBuffer)
	{
		Asura_Direct3D::D3DDevice->SetStreamSource(0,pxCharacter->m_pxVertexBuffer,0,sizeof(Asura_Direct3D_CharacterVertex));

		// VGP Only upload the bones once
		SetBoneVertexShaderConstants(pxCharacter);
	}

	// (SB - 16/11/06) Shader override hook
	if (s_pfnSetupPixelShader)
	{
		s_pfnSetupPixelShader();
	}

	Asura_Direct3D::D3DDevice->SetIndices(pxCharacter->m_pxIndexBuffer);
	Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(
				pxCharacter->m_ePrimitiveType,
				0,
				0,
				pxCharacter->m_uNumberOfVertices,
				0,
				pxCharacter->m_uNumberOfTriangles);

	// Reflective pass
	if(Asura_Material_System::IsMaterialReflective(pxCharacter->m_iMaterialIndex))
	{
		if (	Asura_Direct3D_Caps::GetHardwareVertexShaderVersion() > 1.1f ||
				pxCharacter->m_ucRenderBoneCount > 28)
		{
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES_REFLECTIVE);
		}
		else
		{
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES_REFLECTIVE_VS1);
		}

		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_GLOWING);
		Asura_Render::CurrentStates.RequestTextureIndex( 1, Asura_Render::GetReflectanceMap());
		Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_REFLECTIVE);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR,0x00000000);
		
		if( fAlpha < 1.0f )
		{
			// We need to modulate the texture alpha with the diffuse. Unmunged at the end of the
			// function when the texture op is reset
			Asura_Direct3D::D3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
		}

		D3DXMATRIX xReflectiveMat;
		D3DXMATRIX matView, xTransposedView;
		Asura_Direct3D::D3DDevice->GetTransform( D3DTS_VIEW, &matView );
		D3DXMatrixTranspose( &xTransposedView, &matView );
		matView._14 = matView._24 = matView._34 = 0.0f;
		matView._41 = matView._42 = matView._43 = 0.0f;
		D3DXMatrixInverse( &matView,  NULL, &matView );

		xReflectiveMat._11 = 0.5f; xReflectiveMat._12 = 0.0f; xReflectiveMat._13 = 0.0f; xReflectiveMat._14 = 0.0f; 
		xReflectiveMat._21 = 0.0f; xReflectiveMat._22 = -0.5f; xReflectiveMat._23 = 0.0f; xReflectiveMat._24 = 0.0f; 
		xReflectiveMat._31 = 0.0f; xReflectiveMat._32 = 0.0f; xReflectiveMat._33 = 1.0f; xReflectiveMat._34 = 0.0f; 
		xReflectiveMat._41 = 0.5f; xReflectiveMat._42 = 0.5f; xReflectiveMat._43 = 0.0f; xReflectiveMat._44 = 1.0f; 

		D3DXMatrixMultiplyTranspose(&xReflectiveMat,&matView,&xReflectiveMat);
		
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(8, xTransposedView, 4);
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(12, xReflectiveMat, 2);

		Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(pxCharacter->m_ePrimitiveType,0,0,pxCharacter->m_uNumberOfVertices,0,pxCharacter->m_uNumberOfTriangles);
	}

	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_WHITE);

	// (SB - 16/11/06) Shader override hook
	if (s_pfnRevertPixelShader)
	{
		s_pfnRevertPixelShader();
	}

	if(Asura_Fog::IsFogRenderable())Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);

	D3DPERFCALL(EndEvent());
}

#ifdef ASURA_TOOLS
void Asura_Direct3D_CharacterRenderer::RenderBasic_Outline(const Asura_Direct3D_Character_Strip* pxCharacter,  const Asura_Vector_3& xObjectCentre, u_int uColour)
{
	D3DPERFCALL(BeginEvent(0xffa0573b, L"Character RenderBasic_Outline"));
	switch(s_iMaxBonesPerVertex)
	{
		case 1 :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_1BONE);
			break;
		case 2 :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_2BONES);
			break;
		case 3 :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_3BONES);
			break;
		default :
			Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_ORDER2SH_4BONES);
			break;
	}
		
	if ((uColour >> 24) != 0xff)
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
	}
	else
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_OFF);
	}

	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);
	Asura_Render::CurrentStates.RequestAlphaTest(false);
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS_2X);

	Asura_Render::CurrentStates.RequestTextureIndex(0, ASURA_TEXTURE_NONE );

	if(Asura_Fog::IsFogRenderable())Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
	
	
	SetViewVertexShaderConstants();		
	SetLightVertexShaderConstants_ForOutline(uColour, GetFogValue(xObjectCentre));
//DeadCode VGP 20/03/2006 	SetBoneVertexShaderConstants();	

	if(pxCharacter->m_pxVertexBuffer)
	{
		Asura_Direct3D::D3DDevice->SetStreamSource(0,pxCharacter->m_pxVertexBuffer,0,sizeof(Asura_Direct3D_CharacterVertex));

		// VGP Only upload the bones once
		SetBoneVertexShaderConstants(pxCharacter);
	}
	Asura_Direct3D::D3DDevice->SetIndices(pxCharacter->m_pxIndexBuffer);

	Asura_Render::CurrentStates.RequestWireFrameMode(true);
	Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(pxCharacter->m_ePrimitiveType,0,0,pxCharacter->m_uNumberOfVertices,0,pxCharacter->m_uNumberOfTriangles);
	Asura_Render::CurrentStates.RequestWireFrameMode(Asura_Render::GetWireframe());

	if(Asura_Fog::IsFogRenderable())Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
	D3DPERFCALL(EndEvent());
}
#endif //ASURA_TOOLS

void Asura_Direct3D_CharacterRenderer::RenderShadow(const Asura_Hierarchy_Skin* pxSkin, const Asura_Vector_3& xPosition)
{
	Asura_Direct3D_CharacterRender_Handler&	xHandler	= GetCurrentHandler();
	const Asura_Direct3D_Character_Strip*	pxCharacter = xHandler.GetCharacterStrip(pxSkin);

	// KJL - 13/04/2003 15:24:24 - check to see if we have a character strip for this model
	while (pxCharacter)
	{
		Asura_Render::AddToTrianglesRendered(pxCharacter->m_uNumberOfTriangles);
			
		Asura_Direct3D_Shaders::RequestVertexShader(ASURA_D3D_VS_SKIN_PLANARSHADOW);

		switch(pxCharacter->m_eStripType)
		{
			case ASURA_D3D_CHARACTER_STRIP_BASIC:
				if(pxCharacter->m_pxVertexBuffer)
				{
					Asura_Direct3D::D3DDevice->SetStreamSource(0,pxCharacter->m_pxVertexBuffer,0,sizeof(Asura_Direct3D_CharacterVertex));
					SetBoneVertexShaderConstants(pxCharacter);
				}
				break;
			default:
				Asura_Assert(0, "Unknown strip type");
				break;
		}
		Asura_Direct3D::D3DDevice->SetIndices(pxCharacter->m_pxIndexBuffer);

		Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);
		Asura_Render::CurrentStates.RequestTranslucencyMode(ASURA_TRANSLUCENCY_NORMAL);
		Asura_Render::CurrentStates.RequestAlphaTest(false);
		Asura_Render::CurrentStates.RequestTextureIndex(0, ASURA_TEXTURE_NONE );
		Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
		Asura_Render::CurrentStates.RequestZBufferEnabled(true);
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(false);
		Asura_Render::CurrentStates.RequestZBufferCompareMode(ASURA_ZBUFFERCMP_LESSEQUAL);

		Asura_Render::CurrentStates.RequestStencilEnabled(true);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INVERT);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
		Asura_Direct3D::D3DDevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL);
		
		SetViewVertexShaderConstants();
		// No need for lighting info
//DeadCode VGP 20/03/2006 		SetBoneVertexShaderConstants();

		{
			// Set colour and position

			float fFogDensity = Asura_Fog::GetFogDensityAtDistance((xPosition - (*Asura_Camera::GetPosition())).Magnitude());
			Asura_RGBA xFogColour = Asura_Fog::GetFogColour();
			xFogColour *= fFogDensity;

			FLOAT afShadowColour[4] = {xFogColour.m_fR, xFogColour.m_fG, xFogColour.m_fB, 0.25f*(1-fFogDensity) + xFogColour.m_fA};
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 4, afShadowColour, 1);
			FLOAT afShadowPosition[4] = {0.0f,xPosition.y,0.0f,1.0f};
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 5, afShadowPosition, 1);
		}

		
		Asura_Direct3D::D3DDevice->DrawIndexedPrimitive(pxCharacter->m_ePrimitiveType,0,0,pxCharacter->m_uNumberOfVertices,0,pxCharacter->m_uNumberOfTriangles);

		// Restore state settings
		Asura_Render::CurrentStates.RequestStencilEnabled(false);

		// (AAM - 01.12.2004) Some rendering functions don't correctly request the z write state they need,
		// so we have to preserve it
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled( true );

		pxCharacter = pxCharacter->m_pxNextStrip;
	}
}

void Asura_Direct3D_CharacterRenderer::SetViewVertexShaderConstants()
{
	D3DXMATRIX mat;
	D3DXMATRIX matView, matProj;

	Asura_Direct3D::D3DDevice->GetTransform( D3DTS_VIEW,       &matView );
	Asura_Direct3D::D3DDevice->GetTransform( D3DTS_PROJECTION, &matProj );

    D3DXMatrixMultiply( &mat, &matView, &matProj );
    D3DXMatrixTranspose( &mat, &mat );

    Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(  0, (float*)&mat,  4 );
}

void Asura_Direct3D_CharacterRenderer::SetLightVertexShaderConstants(float fAlpha, bool bSpecular, float fFog, bool bReflective, int iMaterialID)
{	
	float afLights[7][4];
	
	const Asura_SphericalHarmonics_Order2_RGB &xSH = Asura_GI_Solution::s_xSHForCurrentRender;

	afLights[0][0] = xSH.m_afRedCoefficients[1];
	afLights[0][1] = xSH.m_afRedCoefficients[2];
	afLights[0][2] = xSH.m_afRedCoefficients[3];
	afLights[0][3] = xSH.m_afRedCoefficients[0];

	afLights[1][0] = xSH.m_afGreenCoefficients[1]; 
	afLights[1][1] = xSH.m_afGreenCoefficients[2]; 
	afLights[1][2] = xSH.m_afGreenCoefficients[3]; 
	afLights[1][3] = xSH.m_afGreenCoefficients[0]; 

	afLights[2][0] = xSH.m_afBlueCoefficients[1]; 
	afLights[2][1] = xSH.m_afBlueCoefficients[2];
	afLights[2][2] = xSH.m_afBlueCoefficients[3];
	afLights[2][3] = xSH.m_afBlueCoefficients[0];

	// space for fog and stuff

	afLights[3][0] = 0.5f;
	afLights[3][1] = fFog; //fog
	afLights[3][2] = 0.5f;
	afLights[3][3] = fAlpha;

	if(bSpecular)
	{
		afLights[4][0] = Asura_Camera::GetPosition()->x;
		afLights[4][1] = Asura_Camera::GetPosition()->y;
		afLights[4][2] = Asura_Camera::GetPosition()->z;
		afLights[4][3] = 0.5f;

		// Calculate brightest point on SH
		Asura_RGBA xLightColour;
		Asura_Vector_3 xLightDir;
		xSH.GetBrightestDirection(xLightDir);
		// Add to constants
		afLights[5][0] = xLightDir.x;
		afLights[5][1] = xLightDir.y;
		afLights[5][2] = xLightDir.z;
		afLights[5][3] = 0.f;

		// Calculate colour at brightest point on SH
		xLightColour.m_fR = xSH.m_afRedCoefficients[1] * xLightDir.x
							+ xSH.m_afRedCoefficients[2] * xLightDir.y
							+ xSH.m_afRedCoefficients[3] * xLightDir.z
							+ xSH.m_afRedCoefficients[0];
		xLightColour.m_fG = xSH.m_afGreenCoefficients[1] * xLightDir.x
							+ xSH.m_afGreenCoefficients[2] * xLightDir.y
							+ xSH.m_afGreenCoefficients[3] * xLightDir.z
							+ xSH.m_afGreenCoefficients[0];
		xLightColour.m_fB = xSH.m_afBlueCoefficients[1] * xLightDir.x
							+ xSH.m_afBlueCoefficients[2] * xLightDir.y
							+ xSH.m_afBlueCoefficients[3] * xLightDir.z
							+ xSH.m_afBlueCoefficients[0];
		xLightColour.m_fA = 1.f;
		xLightColour.Clamp();

		// Add to constants
		afLights[6][0] = xLightColour.m_fR;
		afLights[6][1] = xLightColour.m_fG;
		afLights[6][2] = xLightColour.m_fB;
		afLights[6][3] = 1.f;

		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 4, (float*)afLights, 7 );

		// Set pixel shader constants
		Asura_Direct3D::D3DDevice->SetPixelShaderConstantF( 0, &afLights[6][0], 1);
		const Asura_RGBA& xFogColour = Asura_Fog::GetFogColour();
		Asura_RGBA xScaledColour = xFogColour * (1.f - Asura_Maths::ClampToRange(fFog, 0.f, 1.f));

		float afFog[4] = {xScaledColour.m_fR, xScaledColour.m_fG, xScaledColour.m_fB, Asura_Maths::ClampToRange(fFog, 0.f, 1.f) };
		Asura_Direct3D::D3DDevice->SetPixelShaderConstantF( 1, &afFog[0], 1);
		const Asura_Material* pxMaterial = Asura_Material_System::GetMaterial(iMaterialID);
		Asura_RGBA xSpecularColour;
		xSpecularColour.FromUIntARGB(pxMaterial ? pxMaterial->m_uSpecularColour : 0xffffffff);
		const float afSpecularColour[4] = {xSpecularColour.m_fR, xSpecularColour.m_fG, xSpecularColour.m_fB, 1.f };
		Asura_Direct3D::D3DDevice->SetPixelShaderConstantF( 2, &afSpecularColour[0], 1); // Set specular colour
		Asura_Direct3D::D3DDevice->SetPixelShaderConstantF( 3, &afLights[3][3], 1); // Set the alpha for the pixel shader
	}
	else if(bReflective)
	{
		D3DXMATRIX xReflectiveMat;
		D3DXMATRIX matView, xTransposedView;
		Asura_Direct3D::D3DDevice->GetTransform( D3DTS_VIEW, &matView );
		D3DXMatrixTranspose( &xTransposedView, &matView );
		matView._14 = matView._24 = matView._34 = 0.0f;
		matView._41 = matView._42 = matView._43 = 0.0f;
		D3DXMatrixInverse( &matView,  NULL, &matView );

		xReflectiveMat._11 = 0.5f; xReflectiveMat._12 = 0.0f; xReflectiveMat._13 = 0.0f; xReflectiveMat._14 = 0.0f; 
		xReflectiveMat._21 = 0.0f; xReflectiveMat._22 = -0.5f; xReflectiveMat._23 = 0.0f; xReflectiveMat._24 = 0.0f; 
		xReflectiveMat._31 = 0.0f; xReflectiveMat._32 = 0.0f; xReflectiveMat._33 = 1.0f; xReflectiveMat._34 = 0.0f; 
		xReflectiveMat._41 = 0.5f; xReflectiveMat._42 = 0.5f; xReflectiveMat._43 = 0.0f; xReflectiveMat._44 = 1.0f; 

		D3DXMatrixMultiplyTranspose(&xReflectiveMat,&matView,&xReflectiveMat);

		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 8, xTransposedView, 3 );
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 11, xReflectiveMat, 2 );
	}
	else
	{
		Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 4, (float*)afLights, 4 );
	}
}

#ifdef ASURA_TOOLS
void Asura_Direct3D_CharacterRenderer::SetLightVertexShaderConstants_ForOutline(u_int uColour, float fFog)
{	
	float afLights[28];
	
	afLights[0] = 0.0f;
	afLights[1] = 0.0f;
	afLights[2] = 0.0f;
	afLights[3] = ((uColour >> 16) & 0xff)/255.0f;

	afLights[4] = 0.0f; 
	afLights[5] = 0.0f; 
	afLights[6] = 0.0f; 
	afLights[7] = ((uColour >> 8) & 0xff)/255.0f; 

	afLights[8] = 0.0f; 
	afLights[9] = 0.0f;
	afLights[10] = 0.0f;
	afLights[11] = ((uColour >> 0) & 0xff)/255.0f;

	// space for fog and stuff

	afLights[12] = 0.5f;
	afLights[13] = fFog; //fog
	afLights[14] = 0.5f;
	afLights[15] = ((uColour >> 24) & 0xff)/255.0f;

	
	Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 4, (float*)afLights, 4 );
	
}
#endif // ASURA_TOOLS

void Asura_Direct3D_CharacterRenderer::SetBoneVertexShaderConstants(const Asura_Direct3D_Character_Strip* pxStrip)
{
	if (!pxStrip || !pxStrip->m_ucRenderBoneCount)
	{
		// Bone 0 is never uploaded
		int iConstant = 15;

		for (int iBone = 1; iBone < Asura_Hierarchy_System::s_pxSkin->NumberOfBones; iBone++)
		{
//DeadCode VGP 20/03/2006 			FLOAT v1[4] = {Asura_Hierarchy_System::s_axBoneOri[iBone].e11,Asura_Hierarchy_System::s_axBoneOri[iBone].e12,Asura_Hierarchy_System::s_axBoneOri[iBone].e13,Asura_Hierarchy_System::s_axBonePos[iBone].x};
//DeadCode VGP 20/03/2006 			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( iConstant++, v1, 1);
//DeadCode VGP 20/03/2006 
//DeadCode VGP 20/03/2006 			FLOAT v2[4] = {Asura_Hierarchy_System::s_axBoneOri[iBone].e21,Asura_Hierarchy_System::s_axBoneOri[iBone].e22,Asura_Hierarchy_System::s_axBoneOri[iBone].e23,Asura_Hierarchy_System::s_axBonePos[iBone].y};
//DeadCode VGP 20/03/2006 			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( iConstant++, v2, 1);
//DeadCode VGP 20/03/2006 
//DeadCode VGP 20/03/2006 			FLOAT v3[4] = {Asura_Hierarchy_System::s_axBoneOri[iBone].e31,Asura_Hierarchy_System::s_axBoneOri[iBone].e32,Asura_Hierarchy_System::s_axBoneOri[iBone].e33,Asura_Hierarchy_System::s_axBonePos[iBone].z};
//DeadCode VGP 20/03/2006 			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( iConstant++, v3, 1);

			// VGP 15Mar2006 Reduce number of SetVertexShaderConstantF(), they're not free.
#if defined(ASURA_HIERARCHY_QUAD)
			float	afData[4][4];

			Asura_Hierarchy_System::s_axBoneTransforms[iBone].StoreRowsUnaligned(&afData[0][0]);
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(iConstant, &afData[0][0], 3);
#else
			FLOAT v[3][4] = 
			{
				Asura_Hierarchy_System::s_axBoneOri[iBone].e11,Asura_Hierarchy_System::s_axBoneOri[iBone].e12,Asura_Hierarchy_System::s_axBoneOri[iBone].e13,Asura_Hierarchy_System::s_axBonePos[iBone].x,
				Asura_Hierarchy_System::s_axBoneOri[iBone].e21,Asura_Hierarchy_System::s_axBoneOri[iBone].e22,Asura_Hierarchy_System::s_axBoneOri[iBone].e23,Asura_Hierarchy_System::s_axBonePos[iBone].y,
				Asura_Hierarchy_System::s_axBoneOri[iBone].e31,Asura_Hierarchy_System::s_axBoneOri[iBone].e32,Asura_Hierarchy_System::s_axBoneOri[iBone].e33,Asura_Hierarchy_System::s_axBonePos[iBone].z
			};

			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(iConstant, &v[0][0], 3);
#endif	// defined(ASURA_HIERARCHY_QUAD)
			iConstant += 3;
		}
	}
	else
	{
		Asura_Assert(pxStrip->m_aucRenderBones, "No render bone map");

		int iConstant = 12;

		for (int iRenderBone = 0; iRenderBone != pxStrip->m_ucRenderBoneCount; ++iRenderBone)
		{
			u_char ucBoneIndex = pxStrip->m_aucRenderBones[iRenderBone];

#if defined(ASURA_HIERARCHY_QUAD)
			float	afData[4][4];

			Asura_Hierarchy_System::s_axBoneTransforms[ucBoneIndex].StoreRowsUnaligned(&afData[0][0]);
			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(iConstant, &afData[0][0], 3);
#else
			FLOAT v[3][4] = 
			{
				Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e11,Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e12,Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e13,Asura_Hierarchy_System::s_axBonePos[ucBoneIndex].x,
				Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e21,Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e22,Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e23,Asura_Hierarchy_System::s_axBonePos[ucBoneIndex].y,
				Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e31,Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e32,Asura_Hierarchy_System::s_axBoneOri[ucBoneIndex].e33,Asura_Hierarchy_System::s_axBonePos[ucBoneIndex].z
			};

			Asura_Direct3D::D3DDevice->SetVertexShaderConstantF(iConstant, &v[0][0], 3);
#endif	// defined(ASURA_HIERARCHY_QUAD)
			iConstant += 3;
		}
	}
}

void Asura_Direct3D_CharacterRenderer::SetTextureAnimVertexShaderConstants(const class Asura_Animation* pxAnim,int iMaterialID)
{
	Asura_TextureAnimationOutput xTextureAnim;
	Asura_Material_System::GetTextureAnimationState(iMaterialID,pxAnim->GetTextureAnimController(),&xTextureAnim);

	float afTextureAnimConstants[4] = {xTextureAnim.m_fUOffset,xTextureAnim.m_fVOffset,0,0};
	Asura_Direct3D::D3DDevice->SetVertexShaderConstantF( 11, afTextureAnimConstants, 1);
}

float Asura_Direct3D_CharacterRenderer::GetFogValue(const Asura_Vector_3 &xObjectCentre)
{
	return 1.f - Asura_Fog::GetFogDensityAtDistance((xObjectCentre - (*Asura_Camera::GetPosition())).Magnitude());
}

void Asura_Direct3D_CharacterRenderer::PreReset()
{
}

void Asura_Direct3D_CharacterRenderer::PostReset()
{
}

Asura_Direct3D_Character_Strip::~Asura_Direct3D_Character_Strip()
{
	if (m_pxVertexBuffer)
	{
		m_pxVertexBuffer->Release();
	}

	if (m_pxIndexBuffer)
	{
		m_pxIndexBuffer->Release();
	}

	if (m_pxNextStrip)
	{
		delete m_pxNextStrip;
	}

	if (m_aucRenderBones)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_SKINNED_MESH);

		Asura_ResourceDeallocateArray(m_aucRenderBones);

		Asura_ResourcePopType();
	}
}

#endif //!defined(ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER) || !ASURA_PC_DISABLE_PRESM3_SMOOTH_SKIN_RENDERER