/**
*
* \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_Hierarchy.h"

#include "Debugging/Asura_Assert.h"

#include "Rendering/Asura_PSP_CharacterRenderer.h"
#include "Rendering/Asura_PSP_ObjectHierarchyRenderer.h"
#include "Rendering/Asura_PSP_Filter.h"

#if defined (ASURA_USE_SHADOWDECALS)
#include "SpecialFX/Asura_ShadowDecal_System.h"
#endif

#include "Animation/Asura_Animation.h"
#include "Camera/Asura_Camera.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Module.h"
#include "Core/Asura_Material_System.h"
#include "Core/Asura_LOS.h"

#include "Debugging/Asura_PSP_Profiling.h"
#include "Debugging/Asura_Logfile.h"

#include <libvfpu.h>
#include <kernel.h>
#include <limits.h>
#include <math.h>
#include <psptypes.h>

#ifdef ASURA_HIERARCHY_QUAD

// RJT TODO: Blatantly shouldn't be here! Find the appropriate place...
#define ASURA_HIERARCHY_QUAD_BBTEST
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
class Asura_ABounding_Box
{
public:

	// Constructor(s)
	Asura_ABounding_Box() {}

	// Reset to defaults
	void Reset()
	{
		// RJT TODO: Simplify (static members that we simply assign?)
		m_xMin.SetX( Asura_Maths::LargeFloat );
		m_xMin.SetY( Asura_Maths::LargeFloat );
		m_xMin.SetZ( Asura_Maths::LargeFloat );
		m_xMin.SetW( Asura_Maths::LargeFloat );
		m_xMax.SetX( -Asura_Maths::LargeFloat );
		m_xMax.SetY( -Asura_Maths::LargeFloat );
		m_xMax.SetZ( -Asura_Maths::LargeFloat );
		m_xMax.SetW( -Asura_Maths::LargeFloat );
	}

	// Set from the specified parameters
	void Set( const Asura_AVector4& xPoint )
	{
		m_xMin.Min(xPoint, m_xMin);
		m_xMax.Max(xPoint, m_xMax);
	}

	// Accessor(s)
	const Asura_AVector4& GetMin() const		{ return m_xMin; }
	const Asura_AVector4& GetMax() const		{ return m_xMax; }

private:

	// Member(s) (private)
	Asura_AVector4	m_xMin;
	Asura_AVector4	m_xMax;
};
#endif	// ASURA_HIERARCHY_QUAD_BBTEST
#endif

/// 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 )
{ 
#ifdef ASURA_PSP_PROFILING
		Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_ENTITY_START ); 
#endif // #ifdef ASURA_PSP_PROFILING

		//Get the bounding box to use for lighting
		Asura_Bounding_Box xLightingBB = s_pxSkin->GetBB();
		//Displace the bounding box's 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)
		Asura_Vector_3	xPosition = s_pxSkin->m_pxBoneRestPoseTransforms[0].GetTranslation();
		xLightingBB.Displace(GetWorldspaceBonePos(0)-xPosition);
#else
		xLightingBB.Displace(GetWorldspaceBonePos(0)-s_pxSkin->BoneRestPosition[0]);
#endif

#ifndef ASURA_HIERARCHY_QUAD
#if defined (ASURA_USE_SHADOWDECALS)
		/// Kevin Lea - 26/02/2002 15:06:58 - make bone transforms valid for model space
		Asura_Bounding_Box xBB;
		xBB.Reset();
#endif //#if defined (ASURA_USE_SHADOWDECALS)

		// KOR - using shorts for vertex positions puts us into the range (-1, 1).
		// Since our smooth skins are generally 2m high, I've scaled it to the range (-4, 4) to be safe.
		// If this is changed, you will need to change the export scale in Tools_SmoothSkin_PSP_Vertex::Set()
		// (NY - 15.02.2007) changed to 8m to match ps2 (which also packs into 16bits, to accuracy is presumably ok) since simpsons has skins over 4m tall!
		const float fScale = 8.f;

		for (int b=0; b<s_pxSkin->NumberOfBones; ++b)
		{
#if (defined ASURA_PSP_USE_VFPU_ASSEMBLER && defined ASURA_PSP_ASM_USE_SN_SYNTAX)

			bool bTrue = true;
//#ifdef 0
			Asura_Vector_3 xWorldPos;

			//AJG - 14/02/05 - VFPU version to save us some calculation time. Stores the values directly in PSP bone pos and ori matrix array, pre-scaled.
			// See Kim's note about scale below, (copied from character renderer where this was originally done)
			// KOR - 12/07/2005 - TODO: this seems to have stopped working with the latest assembler, so needs to be fixed up
			__asm__ (
//				".set			push\n"					// save assember option
//				".set			noreorder\n"			// suppress reordering
				"lv.s s000, 0+%2 \n" // Load s_axBoneOri[b]
				"lv.s s010, 4+%2 \n"
				"lv.s s020, 8+%2 \n"
				"lv.s s001, 12+%2 \n"
				"lv.s s011, 16+%2 \n"
				"lv.s s021, 20+%2 \n"
				"lv.s s002, 24+%2 \n"
				"lv.s s012, 28+%2 \n"
				"lv.s s022, 32+%2 \n"
				"lv.s s030, 0+%3 \n" // Load s_axBonePos[b]
				"lv.s s031, 4+%3 \n"
				"lv.s s032, 8+%3 \n"
				"lv.s s100, 0+%4 \n" // Load *s_pxOri
				"lv.s s110, 4+%4 \n"
				"lv.s s120, 8+%4 \n"
				"lv.s s101, 12+%4 \n"
				"lv.s s111, 16+%4 \n"
				"lv.s s121, 20+%4 \n"
				"lv.s s102, 24+%4 \n"
				"lv.s s112, 28+%4 \n"
				"lv.s s122, 32+%4 \n"
				"lv.s s130, 0+%5 \n" // Load *s_pxPos
				"lv.s s131, 4+%5 \n"
				"lv.s s132, 8+%5 \n"
				"lv.s s200, 0+%6 \n" // Load s_pxSkin->BoneRestOrientation[b]
				"lv.s s210, 4+%6 \n"
				"lv.s s220, 8+%6 \n"
				"lv.s s201, 12+%6 \n"
				"lv.s s211, 16+%6 \n"
				"lv.s s221, 20+%6 \n"
				"lv.s s202, 24+%6 \n"
				"lv.s s212, 28+%6 \n"
				"lv.s s222, 32+%6 \n"
				"lv.s s230, 0+%7 \n" // Load s_pxSkin->BoneRestPosition[b]
				"lv.s s231, 4+%7 \n"
				"lv.s s232, 8+%7 \n"
				"lv.s s233, 0+%8 \n" // Load s_bBonesInWorldspace
				"lv.s s223, 0+%9 \n" // Load fScale
				"vi2f.s s233, s233, 0 \n" // Convert bool to float
				"vzero.t r003 \n" // Zero bottom row of matrices
				"vzero.t r103 \n"
				"vone.s s033 \n" // One value 33 of matrices
				"vone.s s133 \n"
				"vcmp.s NZ, s233 \n" // Is s_bBonesInWorldspace non-zero?
				"bvt 0, 0f \n" // Branch past first multiplication if true
				"vnop \n"
				"vmmul.q m300, m100, m000 \n" // 4*4 matrix multiply, hence get *s_pxOri * s_axBoneOri[b] and (*s_pxOri * s_axBonePos[b]) + *s_pxPos (Equivalent to GetWorldspaceBoneOri and GetWorldspaceBonePos when s_bBonesInWorldspace is false)
				"b 1f \n" // Unconditional branch
				"vnop \n"
			"0:\n"
				"vmmov.q m300, m000 \n" // Copy s_axBoneOri[b] and s_axBonePos[b] (Equivalent to GetWorldspaceBoneOri and GetWorldspaceBonePos when s_bBonesInWorldspace is true)
			"1:\n"
				"vmmul.t m000, m300, m200 \n" // GetWorldspaceBoneOri(b) * s_pxSkin->BoneRestOrientation[b]
				"vtfm3.t c130, e000, c230 \n" // s_pxSkin->BoneRestPosition[b]*s_axBoneOri[b]
				"vmscl.t m100, m000, s223 \n" // Scale orientation matrix
				"vsub.t c130, c330, c130 \n" // xWorldPos - (s_pxSkin->BoneRestPosition[b]*s_axBoneOri[b])
				"sv.s s100, 0+%0 \n"
				"sv.s s101, 4+%0 \n"
				"sv.s s102, 8+%0 \n"
				"sv.s s103, 12+%0 \n"
				"sv.s s110, 16+%0 \n"
				"sv.s s111, 20+%0 \n"
				"sv.s s112, 24+%0 \n"
				"sv.s s113, 28+%0 \n"
				"sv.s s120, 32+%0 \n"
				"sv.s s121, 36+%0 \n"
				"sv.s s122, 40+%0 \n"
				"sv.s s123, 44+%0 \n"
				"sv.s s130, 48+%0 \n"
				"sv.s s131, 52+%0 \n"
				"sv.s s132, 56+%0 \n"
				"sv.s s133, 60+%0 \n"
				"sv.s s330, 0+%1 \n"
				"sv.s s331, 4+%1 \n"
				"sv.s s332, 8+%1 \n"
//				".set			pop\n"		// restore assember option
				: "=m" (Asura_PSP_CharacterRenderer::s_axBonePosAndOri[b]) // Bone pos and ori output
				, "=m" (xWorldPos) // World pos for BB set
				: "m" (s_axBoneOri[b])
				, "m" (s_axBonePos[b])
				, "m" (*s_pxOri)
				, "m" (*s_pxPos)
				, "m" (s_pxSkin->BoneRestOrientation[b])
				, "m" (s_pxSkin->BoneRestPosition[b])
				, "m" (bTrue)// was s_bBonesInWorldspace. However, our new smooth skin renderer never needs to translate into worldspace;
							 // it's done using the world matrix instead.
				, "m" (fScale)
				);

#if defined (ASURA_USE_SHADOWDECALS)
			xBB.Set(GetWorldspaceBonePos(b));//xWorldPos * *Asura_Hierarchy_System::s_pxOri);
#endif //#if defined (ASURA_USE_SHADOWDECALS)

#else //#ifdef ASURA_PSP_USE_VFPU_ASSEMBLER
			const Asura_Vector_3 xWorldPos = GetWorldspaceBonePos(b);
#if defined (ASURA_USE_SHADOWDECALS)
			xBB.Set(xWorldPos);
#endif //#if defined (ASURA_USE_SHADOWDECALS)
			Asura_Matrix_3x3& xOri = s_axBoneOri[b];
			Asura_Vector_3& xPos = s_axBonePos[b];
//			xOri = GetWorldspaceBoneOri(b) * s_pxSkin->BoneRestOrientation[b];
//			xPos = xWorldPos - s_pxSkin->BoneRestPosition[b]*s_axBoneOri[b];
			xOri = s_axBoneOri[b] * s_pxSkin->BoneRestOrientation[b];
			xPos = s_axBonePos[b] - s_pxSkin->BoneRestPosition[b]*s_axBoneOri[b];

			Asura_PSP_Matrix4& xMatrix = Asura_PSP_CharacterRenderer::s_axBonePosAndOri[b];

			xMatrix.x.x = xOri.e11 * fScale;
			xMatrix.x.y = xOri.e21 * fScale;
			xMatrix.x.z = xOri.e31 * fScale;
			xMatrix.y.x = xOri.e12 * fScale;
			xMatrix.y.y = xOri.e22 * fScale;
			xMatrix.y.z = xOri.e32 * fScale;
			xMatrix.z.x = xOri.e13 * fScale;
			xMatrix.z.y = xOri.e23 * fScale;
			xMatrix.z.z = xOri.e33 * fScale;
			xMatrix.w.x = xPos.x;
			xMatrix.w.y = xPos.y;
			xMatrix.w.z = xPos.z;

			xMatrix.w.w = 1.f;

			xMatrix.x.w = 0;
			xMatrix.y.w = 0;
			xMatrix.z.w = 0;
#endif //#else #ifdef ASURA_PSP_USE_VFPU_ASSEMBLER
		}
#else	// ASURA_HIERARCHY_QUAD
#ifdef ASURA_HIERARCHY_QUAD_VFPU
		Asura_AMatrix4x4 *pxBones = reinterpret_cast< Asura_AMatrix4x4 * >( reinterpret_cast< uint32 >( Asura_PSP_CharacterRenderer::s_axBonePosAndOri ) | 0x40000000 );
#endif
#if defined (ASURA_USE_SHADOWDECALS)
		/// Kevin Lea - 26/02/2002 15:06:58 - make bone transforms valid for model space
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
		Asura_ABounding_Box xABB;
#else
		Asura_Bounding_Box xBB;
#endif
		// RJT NOTE: This is a workaround for a code generation issue with GCC4 (psp-gcc 2.5.2 anyhow)
#ifdef GCC4_BUGGED
		Asura_AMatrix4x4 xBaseTransform = s_xBaseTransform;
#else
		Asura_AMatrix4x4& xBaseTransform = s_xBaseTransform;
#endif
		if (uCamVisFlags && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW))
		{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
			xABB.Reset();
#else
			xBB.Reset();
#endif
			if( s_bBonesInWorldspace )
			{
				for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
				{
					Asura_AMatrix4x4	xBoneRestPoseScaledInverseTransform;

					// ART - 06/07/07 - Maybe this can be done better, but scaling is a PSP only thing so I've
					// moved the code from where the inverse-transforms are calculated to the point where they're
					// used. Otherwise the scale gets applied to BB calculations and messes up the maths elsewhere.
#if 0
					g_xScaleTransform.Apply(s_pxSkin->m_pxBoneRestPoseInverseTransforms[u], xBoneRestPoseScaledInverseTransform);
#else
					Asura_AVector4	xTranslation;

					for(u_int uColumn = 0; uColumn < 3; uColumn++)
					{
						Asura_AVector4	xColumn = s_pxSkin->m_pxBoneRestPoseInverseTransforms[u].GetColumn(uColumn);
						xColumn	*= 8.0f;
						xBoneRestPoseScaledInverseTransform.SetColumn(uColumn, xColumn);
					}

					xTranslation = s_pxSkin->m_pxBoneRestPoseInverseTransforms[u].GetA4Translation();
					xBoneRestPoseScaledInverseTransform.SetTranslation(xTranslation);
#endif

#ifdef ASURA_HIERARCHY_QUAD_BBTEST
					{
						xABB.Set( s_axBoneTransforms[ u ].GetColumn3() );
					}
#else
					xBB.Set( reinterpret_cast< const Asura_Vector_3& >( s_axBoneTransforms[ u ].GetColumn3() ) );
#endif

#if defined ASURA_HIERARCHY_QUAD_VFPU && defined ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
					// RJT TODO: If we had more allocated registers we could use the vmmul.q instruction
					SNVectorType xA, xB, xC, xD;
					SNMatrixType& rxLHS = reinterpret_cast< SNMatrixType& >( xBoneRestPoseScaledInverseTransform );
					SNMatrixType& rxRHS = reinterpret_cast< SNMatrixType& >( s_axBoneTransforms[ u ] );
					__asm {
						vtfm4.q		xA, rxRHS( exx ), rxLHS( cx )
						vtfm4.q		xB, rxRHS( exx ), rxLHS( cy )
						vtfm4.q		xC, rxRHS( exx ), rxLHS( cz )
						vtfm4.q		xD, rxRHS( exx ), rxLHS( cw )
						sv.q		xA, 0( pxBones ), wb
						sv.q		xB, 16( pxBones ), wb
						sv.q		xC, 32( pxBones ), wb
						sv.q		xD, 48( pxBones ), wb
						addiu		pxBones, 64
					}
#elif defined ASURA_HIERARCHY_QUAD_VFPU && defined ASURA_PSP_USE_VFPU_ASSEMBLER
					__asm__ (
						".set		push\n"							// Save assembler option
						".set		noreorder\n"					// Suppress reordering
						"lv.q		c000, 0 + %1\n"
						"lv.q		c010, 16 + %1\n"
						"lv.q		c020, 32 + %1\n"
						"lv.q		c030, 48 + %1\n"
						"lv.q		c100, 0 + %2\n"
						"lv.q		c110, 16 + %2\n"
						"lv.q		c120, 32 + %2\n"
						"lv.q		c130, 48 + %2\n"
						"vmmul.q	m200, m100, m000\n"
						"sv.q		c200, 0 + %0, wb\n"
						"sv.q		c210, 16 + %0, wb\n"
						"sv.q		c220, 32 + %0, wb\n"
						"sv.q		c230, 48 + %0, wb\n"
						".set		pop\n"							// Restore assembler option
						: "=m"( pxBones[ u ] )
						: "m"( xBoneRestPoseScaledInverseTransform ), "m"( s_axBoneTransforms[ u ] )
					);
#else
					s_axBoneTransforms[u].Apply(xBoneRestPoseScaledInverseTransform, Asura_PSP_CharacterRenderer::s_axBonePosAndOri[u]);
#endif
				}
			}
			else
			{
				for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
				{
					Asura_AMatrix4x4	xBoneRestPoseScaledInverseTransform;

					// ART - 06/07/07 - Maybe this can be done better, but scaling is a PSP only thing so I've
					// moved the code from where the inverse-transforms are calculated to the point where they're
					// used. Otherwise the scale gets applied to BB calculations and messes up the maths elsewhere.
#if 0
					g_xScaleTransform.Apply(s_pxSkin->m_pxBoneRestPoseInverseTransforms[u], xBoneRestPoseScaledInverseTransform);
#else
					Asura_AVector4	xTranslation;

					for(u_int uColumn = 0; uColumn < 3; uColumn++)
					{
						Asura_AVector4	xColumn = s_pxSkin->m_pxBoneRestPoseInverseTransforms[u].GetColumn(uColumn);
						xColumn	*= 8.0f;
						xBoneRestPoseScaledInverseTransform.SetColumn(uColumn, xColumn);
					}

					xTranslation = s_pxSkin->m_pxBoneRestPoseInverseTransforms[u].GetA4Translation();
					xBoneRestPoseScaledInverseTransform.SetTranslation(xTranslation);
#endif

#ifdef ASURA_HIERARCHY_QUAD_BBTEST
					{
						Asura_AVector4 xCol = s_axBoneTransforms[ u ].GetColumn3();
						xABB.Set(  xCol * xBaseTransform );
					}
#else
					Asura_AVector4 xPos( s_axBoneTransforms[ u ].GetColumn3() * xBaseTransform );
					xBB.Set( reinterpret_cast< const Asura_Vector_3& >( xPos ) );
#endif

#if defined ASURA_HIERARCHY_QUAD_VFPU && defined ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
					// RJT TODO: If we had more allocated registers we could use the vmmul.q instruction
					SNVectorType xA, xB, xC, xD;
					SNMatrixType& rxLHS = reinterpret_cast< SNMatrixType& >( xBoneRestPoseScaledInverseTransform );
					SNMatrixType& rxRHS = reinterpret_cast< SNMatrixType& >( s_axBoneTransforms[ u ] );
					__asm {
						vtfm4.q		xA, rxRHS( exx ), rxLHS( cx )
						vtfm4.q		xB, rxRHS( exx ), rxLHS( cy )
						vtfm4.q		xC, rxRHS( exx ), rxLHS( cz )
						vtfm4.q		xD, rxRHS( exx ), rxLHS( cw )
						sv.q		xA, 0( pxBones ), wb
						sv.q		xB, 16( pxBones ), wb
						sv.q		xC, 32( pxBones ), wb
						sv.q		xD, 48( pxBones ), wb
						addiu		pxBones, 64
					}
#elif defined ASURA_HIERARCHY_QUAD_VFPU && defined ASURA_PSP_USE_VFPU_ASSEMBLER
					__asm__ (
						".set		push\n"							// Save assembler option
						".set		noreorder\n"					// Suppress reordering
						"lv.q		c000, 0 + %1\n"
						"lv.q		c010, 16 + %1\n"
						"lv.q		c020, 32 + %1\n"
						"lv.q		c030, 48 + %1\n"
						"lv.q		c100, 0 + %2\n"
						"lv.q		c110, 16 + %2\n"
						"lv.q		c120, 32 + %2\n"
						"lv.q		c130, 48 + %2\n"
						"vmmul.q	m200, m100, m000\n"
						"sv.q		c200, 0 + %0, wb\n"
						"sv.q		c210, 16 + %0, wb\n"
						"sv.q		c220, 32 + %0, wb\n"
						"sv.q		c230, 48 + %0, wb\n"
						".set		pop\n"							// Restore assembler option
						: "=m"( pxBones[ u ] )
						: "m"( xBoneRestPoseScaledInverseTransform ), "m"( s_axBoneTransforms[ u ] )
					);
#else
					s_axBoneTransforms[u].Apply(xBoneRestPoseScaledInverseTransform, Asura_PSP_CharacterRenderer::s_axBonePosAndOri[u]);
#endif
				}
			}
		}
		else
#endif
		{
			for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
			{
				Asura_AMatrix4x4	xBoneRestPoseScaledInverseTransform;

				// ART - 06/07/07 - Maybe this can be done better, but scaling is a PSP only thing so I've
				// moved the code from where the inverse-transforms are calculated to the point where they're
				// used. Otherwise the scale gets applied to BB calculations and messes up the maths elsewhere.
#if 0
				g_xScaleTransform.Apply(s_pxSkin->m_pxBoneRestPoseInverseTransforms[u], xBoneRestPoseScaledInverseTransform);
#else
				Asura_AVector4	xTranslation;

				for(u_int uColumn = 0; uColumn < 3; uColumn++)
				{
					Asura_AVector4	xColumn = s_pxSkin->m_pxBoneRestPoseInverseTransforms[u].GetColumn(uColumn);
					xColumn	*= 8.0f;
					xBoneRestPoseScaledInverseTransform.SetColumn(uColumn, xColumn);
				}

				xTranslation = s_pxSkin->m_pxBoneRestPoseInverseTransforms[u].GetA4Translation();
				xBoneRestPoseScaledInverseTransform.SetTranslation(xTranslation);
#endif

#if defined ASURA_HIERARCHY_QUAD_VFPU && defined ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
				// RJT TODO: If we had more allocated registers we could use the vmmul.q instruction
				SNVectorType xA, xB, xC, xD;
				SNMatrixType& rxLHS = reinterpret_cast< SNMatrixType& >( xBoneRestPoseScaledInverseTransform );
				SNMatrixType& rxRHS = reinterpret_cast< SNMatrixType& >( s_axBoneTransforms[ u ] );
				__asm {
					vtfm4.q		xA, rxRHS( exx ), rxLHS( cx )
					vtfm4.q		xB, rxRHS( exx ), rxLHS( cy )
					vtfm4.q		xC, rxRHS( exx ), rxLHS( cz )
					vtfm4.q		xD, rxRHS( exx ), rxLHS( cw )
					sv.q		xA, 0( pxBones ), wb
					sv.q		xB, 16( pxBones ), wb
					sv.q		xC, 32( pxBones ), wb
					sv.q		xD, 48( pxBones ), wb
					addiu		pxBones, 64
				}
#elif defined ASURA_HIERARCHY_QUAD_VFPU && defined ASURA_PSP_USE_VFPU_ASSEMBLER
				__asm__ (
					".set		push\n"							// Save assembler option
					".set		noreorder\n"					// Suppress reordering
					"lv.q		c000, 0 + %1\n"
					"lv.q		c010, 16 + %1\n"
					"lv.q		c020, 32 + %1\n"
					"lv.q		c030, 48 + %1\n"
					"lv.q		c100, 0 + %2\n"
					"lv.q		c110, 16 + %2\n"
					"lv.q		c120, 32 + %2\n"
					"lv.q		c130, 48 + %2\n"
					"vmmul.q	m200, m100, m000\n"
					"sv.q		c200, 0 + %0, wb\n"
					"sv.q		c210, 16 + %0, wb\n"
					"sv.q		c220, 32 + %0, wb\n"
					"sv.q		c230, 48 + %0, wb\n"
					".set		pop\n"							// Restore assembler option
					: "=m"( pxBones[ u ] )
					: "m"( xBoneRestPoseScaledInverseTransform ), "m"( s_axBoneTransforms[ u ] )
				);
#else
				s_axBoneTransforms[u].Apply(xBoneRestPoseScaledInverseTransform, Asura_PSP_CharacterRenderer::s_axBonePosAndOri[u]);
#endif
			}
		}

#ifdef ASURA_HIERARCHY_QUAD_VFPU
	// Flush VFPU write-back cache now we're done
	#ifdef ASURA_PSP_ASM_USE_SN_SYNTAX_NEW
		__asm {
			vflush
			vnop
		}
	#elif ASURA_PSP_USE_VFPU_ASSEMBLER
		__asm__ (
			".set		push\n"							// Save assembler option
			".set		noreorder\n"					// Suppress reordering
			"vflush\n"
			"vnop\n"
			".set		pop\n"							// Restore assembler option
		);
	#endif

	// RJT NOTE: This make's sure that subsequent reads of this new data come directly from memory
	sceKernelDcacheInvalidateRange( Asura_PSP_CharacterRenderer::s_axBonePosAndOri, sizeof( Asura_AMatrix4x4 ) * s_pxSkin->NumberOfBones );
#endif
#endif	// ASURA_HIERARCHY_QUAD

		Asura_Vector_3 xFirstBonePos = GetWorldspaceBonePos(0);
		for(int i=0; i<Asura_Camera::GetNumberOfCameras(); ++i)
		{
			if((1<<i) & uCamVisFlags)
			{
				Asura_Camera::SetCameraIndex( i );
#ifdef ASURA_HIERARCHY_QUAD
				Asura_PSP_CharacterRenderer::SetViewMatrix( *Asura_Camera::GetOrientation(), *Asura_Camera::GetPosition() );
#else
				Asura_Render::ApplyCameraSettings();
#endif
				//CA 13.02.2007 Get the detail skin from position
				float fDistance = (*Asura_Camera::GetPosition() - xFirstBonePos).Magnitude();

				const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);
	
				Asura_PSP_CharacterRenderer::Render(pxDetailSkin, xLightingBB, fAlpha, iTexIDOverride );
			}
		}	

#if defined (ASURA_USE_SHADOWDECALS)
		if (uCamVisFlags && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW))
		{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
			Asura_Bounding_Box xBB;
			xBB.MinX = xABB.GetMin().GetX();
			xBB.MinY = xABB.GetMin().GetY();
			xBB.MinZ = xABB.GetMin().GetZ();
			xBB.MaxX = xABB.GetMax().GetX();
			xBB.MaxY = xABB.GetMax().GetY();
			xBB.MaxZ = xABB.GetMax().GetZ();
#endif
//			xBB.Displace(*Asura_Hierarchy_System::s_pxPos);
			Asura_ShadowDecal_System::AddShadow( xBB, pxAnim );
		}
#endif

#ifdef ASURA_PSP_PROFILING
		Asura_PSP_Profiling::Signal( Asura_PSP_Profiling::ASURA_PSP_SIGNAL_ENTITY_END ); 
#endif // #ifdef ASURA_PSP_PROFILING
}

#ifndef ASURA_HIERARCHY_QUAD
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*/ )
{
	// First version of alphaing - just disappear
	if (pxAnim && pxAnim->GetAlpha() <= 0.0f)
	{
		return;
	}

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

	Asura_Vector_3 xFirstBonePos = GetWorldspaceBonePos(0);
	if( s_bJustRenderForCurCam )
	{
		//CA 13.02.2007 Get the detail skin from position
		float fDistance = (*Asura_Camera::GetPosition() - xFirstBonePos).Magnitude();
		Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);

		Asura_PSP_ObjectHierarchyRenderer::BeginRender( pxAnim, pxDetailSkin );
		Asura_Vector_3 xWorldPos;
		for(int j=0;j<s_pxSkin->NumberOfBones;++j)
		{
			xWorldPos = GetWorldspaceBonePos(j);
#if defined (ASURA_USE_SHADOWDECALS)
			xBB.Set(xWorldPos);
#endif //#if defined (ASURA_USE_SHADOWDECALS)

			Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, j, xWorldPos, GetWorldspaceBoneOri(j), iTexIDOverride );
		}

		Asura_PSP_ObjectHierarchyRenderer::EndRender();

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

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

			//CA 13.02.2007 Get the detail skin from position
			float fDistance = (*Asura_Camera::GetPosition() - xFirstBonePos).Magnitude();
			Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);

			// KJFL - 2004-02-23 14:45:00 - draw the hierarchy			
			Asura_PSP_ObjectHierarchyRenderer::BeginRender( pxAnim, pxDetailSkin );

			Asura_Vector_3 xWorldPos;
			for(int j=0;j<s_pxSkin->NumberOfBones;++j)
			{
				xWorldPos = GetWorldspaceBonePos(j);
#if defined (ASURA_USE_SHADOWDECALS)
				xBB.Set(xWorldPos);
#endif //#if defined (ASURA_USE_SHADOWDECALS)

				Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, j, xWorldPos, GetWorldspaceBoneOri(j), iTexIDOverride );
			}

			Asura_PSP_ObjectHierarchyRenderer::EndRender();
		}
	}

#if defined (ASURA_USE_SHADOWDECALS)
	if (uCamVisFlags && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW))
	{
		// it's likely that 1 bone skins won't have created a valid BB
		if( (s_pxSkin->NumberOfBones <= 1) || (xBB.CalculateExtents().MagnitudeSquared() == 0.0f) )
		{
			xBB = s_pxSkin->GetBB();
			xBB.Displace( *Asura_Hierarchy_System::s_pxPos );
		}

		Asura_ShadowDecal_System::AddShadow( xBB, pxAnim );
	}
#endif
}
#else	// ASURA_HIERARCHY_QUAD
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*/ )
{
	// First version of alphaing - just disappear
	if (pxAnim && pxAnim->GetAlpha() <= 0.0f)
	{
		return;
	}

#if defined (ASURA_USE_SHADOWDECALS)
	/// Kevin Lea - 26/02/2002 15:06:58 - make bone transforms valid for model space
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
	Asura_ABounding_Box xABB;
#else
	Asura_Bounding_Box xBB;
#endif
#endif //#if defined (ASURA_USE_SHADOWDECALS)

	// RJT NOTE: This is a workaround for a code generation issue with GCC4 (psp-gcc 2.5.2 anyhow)
#ifdef GCC4_BUGGED
	Asura_AMatrix4x4 xBaseTransform = s_xBaseTransform;
#else
	Asura_AMatrix4x4& xBaseTransform = s_xBaseTransform;
#endif

	Asura_Vector_3 xFirstBonePos = GetWorldspaceBonePos(0);
	if( s_bJustRenderForCurCam )
	{
		//CA 13.02.2007 Get the detail skin from position
		float fDistance = (*Asura_Camera::GetPosition() - xFirstBonePos).Magnitude();
		const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);

		Asura_PSP_CharacterRenderer::SetViewMatrix( *Asura_Camera::GetOrientation(), *Asura_Camera::GetPosition() );
		Asura_PSP_ObjectHierarchyRenderer::BeginRender( pxAnim, pxDetailSkin );
#if defined (ASURA_USE_SHADOWDECALS)
		if (uCamVisFlags && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW))
		{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
			xABB.Reset();
#else
			xBB.Reset();
#endif
			if( s_bBonesInWorldspace )
			{
				for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
				{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
					{
						xABB.Set( s_axBoneTransforms[ u ].GetColumn3() );
					}
#else
					xBB.Set( reinterpret_cast< const Asura_Vector_3& >( s_axBoneTransforms[ u ].GetColumn3() ) );
#endif

					Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, s_axBoneTransforms[ u ], iTexIDOverride );
				}
			}
			else
			{
				for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
				{
					// RJT TODO: Separate loop for this using write-back cache?
					Asura_AMatrix4x4 xBoneWorld;

//					xBoneWorld = s_axBoneTransforms[ u ] * xBaseTransform;
					xBaseTransform.Apply(s_axBoneTransforms[u], xBoneWorld);

#ifdef ASURA_HIERARCHY_QUAD_BBTEST
					{
						xABB.Set( xBoneWorld.GetColumn3() );
					}
#else
					xBB.Set( reinterpret_cast< const Asura_Vector_3& >( xBoneWorld.GetColumn3() ) );
#endif

					Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, xBoneWorld, iTexIDOverride );
				}
			}
		}
		else
#endif
		{
			if( s_bBonesInWorldspace )
			{
				for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
				{
					Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, s_axBoneTransforms[ u ], iTexIDOverride );
				}
			}
			else
			{
				for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
				{
					// RJT TODO: Separate loop for this using write-back cache?
					Asura_AMatrix4x4 xBoneWorld;

//					xBoneWorld = s_axBoneTransforms[ u ] * xBaseTransform;
					xBaseTransform.Apply(s_axBoneTransforms[u], xBoneWorld);

					Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, xBoneWorld, iTexIDOverride );
				}
			}
		}
		Asura_PSP_ObjectHierarchyRenderer::EndRender();

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

	for(int i=0; i<Asura_Camera::GetNumberOfCameras(); ++i)
	{
		if((1<<i) & uCamVisFlags)
		{
			Asura_Camera::SetCameraIndex(i);
			Asura_PSP_ObjectHierarchyRenderer::SetViewMatrix( *Asura_Camera::GetOrientation(), *Asura_Camera::GetPosition() );

			//CA 13.02.2007 Get the detail skin from position
			float fDistance = (*Asura_Camera::GetPosition() - xFirstBonePos).Magnitude();
			const Asura_Hierarchy_Skin* pxDetailSkin = s_pxSkin->GetDetailSkinFromDistance(fDistance);

			// KJFL - 2004-02-23 14:45:00 - draw the hierarchy			
			Asura_PSP_ObjectHierarchyRenderer::BeginRender( pxAnim, pxDetailSkin );
#if defined (ASURA_USE_SHADOWDECALS)
			if (uCamVisFlags && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW))
			{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
				xABB.Reset();
#else
				xBB.Reset();
#endif
				if( s_bBonesInWorldspace )
				{
					for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
					{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
						{
							xABB.Set( s_axBoneTransforms[ u ].GetColumn3() );
						}
#else
						xBB.Set( reinterpret_cast< const Asura_Vector_3& >( s_axBoneTransforms[ u ].GetColumn3() ) );
#endif

						Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, s_axBoneTransforms[ u ], iTexIDOverride );
					}
				}
				else
				{
					for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
					{
						// RJT TODO: Separate loop for this using write-back cache?
						Asura_AMatrix4x4 xBoneWorld;

//						xBoneWorld = s_axBoneTransforms[ u ] * xBaseTransform;
						xBaseTransform.Apply(s_axBoneTransforms[u], xBoneWorld);

#ifdef ASURA_HIERARCHY_QUAD_BBTEST
						{
							xABB.Set( xBoneWorld.GetColumn3() );
						}
#else
						xBB.Set( reinterpret_cast< const Asura_Vector_3& >( xBoneWorld.GetColumn3() ) );
#endif
						Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, xBoneWorld, iTexIDOverride );
					}
				}
			}
			else
#endif
			{
				if( s_bBonesInWorldspace )
				{
					for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
					{
						Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, s_axBoneTransforms[ u ], iTexIDOverride );
					}
				}
				else
				{
					for( uint32 u = 0; u < s_pxSkin->NumberOfBones; ++u )
					{
						// RJT TODO: Separate loop for this using write-back cache?
						Asura_AMatrix4x4 xBoneWorld;

//						xBoneWorld = s_axBoneTransforms[ u ] * xBaseTransform;
						xBaseTransform.Apply(s_axBoneTransforms[u], xBoneWorld);

						Asura_PSP_ObjectHierarchyRenderer::Render( pxAnim, pxDetailSkin, u, xBoneWorld, iTexIDOverride );
					}
				}
			}
			Asura_PSP_ObjectHierarchyRenderer::EndRender();
		}
	}

#if defined (ASURA_USE_SHADOWDECALS)
	if (uCamVisFlags && !(uRenderFlags&ASURA_HIERARCHY_RENDER_NO_SHADOW))
	{
#ifdef ASURA_HIERARCHY_QUAD_BBTEST
		Asura_Bounding_Box xBB;
		xBB.MinX = xABB.GetMin().GetX();
		xBB.MinY = xABB.GetMin().GetY();
		xBB.MinZ = xABB.GetMin().GetZ();
		xBB.MaxX = xABB.GetMax().GetX();
		xBB.MaxY = xABB.GetMax().GetY();
		xBB.MaxZ = xABB.GetMax().GetZ();
#endif
		// it's likely that 1 bone skins won't have created a valid BB
		if( (s_pxSkin->NumberOfBones <= 1) || (xBB.CalculateExtents().MagnitudeSquared() == 0.0f) )
		{
			xBB = s_pxSkin->GetBB();
			Asura_AVector4 xCol = xBaseTransform.GetColumn3();
			xBB.Displace( reinterpret_cast< const Asura_Vector_3& >( xCol ) );
		}

		Asura_ShadowDecal_System::AddShadow( xBB, pxAnim );
	}
#endif
}
#endif

void Asura_Hierarchy_System::Platform_RenderSkinShadow( const Asura_Animation& xAnim, const Asura_Shadow& xShadow, const u_int uCamVisFlags, const u_int uRenderFlags )
{
}

void Asura_Hierarchy_System::Platform_RenderHierarchyShadow( const Asura_Animation& xAnim, const Asura_Shadow& xShadow, const u_int uCamVisFlags, const u_int uRenderFlags )
{
}

static inline int GetMaterialIndexForSkin( const Asura_Hierarchy_Skin* pxSkin )
{
	if( pxSkin->IsSkinnedModel() )
	{
		return Asura_PSP_CharacterRenderer::GetMaterialIndex( pxSkin );
	}
	else
	{
		return Asura_PSP_ObjectHierarchyRenderer::GetMaterialIndex( pxSkin );
    }
}

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

void Asura_Hierarchy_System::Platform_Initialise( void )
{
}

void Asura_Hierarchy_System::Platform_Reset( void )
{
}

void Asura_Hierarchy_System::Platform_Shutdown( void )
{
}

bool Asura_Hierarchy_Skin::Platform_IsOpaque( void ) const
{
	return !Asura_Material_System::IsMaterialTransparent(GetMaterialIndexForSkin(this));
}
