//
// Tools_Wii_Export.cpp
//
// Created by KOR - 09 January 2007
//
// Copyright (C) 2007 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Wii/Tools_Wii_Export.h"

// Asura headers
#include "Animation/Asura_Hierarchy.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Mesh/Asura_Env_System.h"
#include "Rendering/Asura_Colour.h"
#include "Rendering/Asura_Module.h"

// Tools headers
#include "MeshProcessing/Tools_EnvironmentProcessor.h"
#include "MeshProcessing/Tools_HierarchyProcessor_MultiMaterial.h"
#include "MeshProcessing/Tools_ObjectProcessor.h"
#include "MeshProcessing/Tools_SmoothSkin_Processor.h"
#include "MeshProcessing/Tools_SmoothSkin_Processor_Wii.h"
#include "Wii/Tools_Wii_DisplayList.h"

HIERARCHY_SET_COLOUR_FN			Tools_Wii_Export::s_HierarchyColorFunc = NULL;
u_int							Tools_Wii_Export::s_uHierarchyExportFlags = ASURA_WII_HIERARCHY_CHUNK_FLAG_NORMALS | ASURA_WII_HIERARCHY_CHUNK_FLAG_UV1 | ASURA_WII_HIERARCHY_CHUNK_FLAG_DISPLAYLIST;
Tools_Wii_DisplayListFactory*		Tools_Wii_Export::s_pxDisplayListGenerator = NULL;
// Default to RGB565 encoding
ENVIRONMENT_ENCODE_COLOUR_FN	Tools_Wii_Export::s_pfnEnvironmentEncodeFunction = ARGB8888ToRGBA6;
u_int							Tools_Wii_Export::s_uEnvironmentExportFlags = EXPORT_FLAG_NORMALS | EXPORT_FLAG_VERTEXCOLOURRGBA6 | EXPORT_FLAG_USE_DISPLAY_LISTS;

const char* g_szCurrentMeshName = NULL;
static u_int g_uHierarchyFractionalBits = 0;

class ToolsSimp_WiiHierarchyDisplayLists : public Tools_Wii_DisplayListFactory
{
public:
	ToolsSimp_WiiHierarchyDisplayLists() {}
protected:
	virtual u_int SizePerVert()
	{
		u_int uSizePerVert = 2; // GXPosition1x16
		if (Tools_Wii_Export::GetHierarchyExportNormals())
		{
			uSizePerVert += 2; // GXNormal1x16
		}
		if (Tools_Wii_Export::GetHierarchyExportColours())
		{
			uSizePerVert += 2; // GXColor1x16;
		}
		else if(Tools_Wii_Export::GetHierarchyExportAlpha())
		{
			uSizePerVert += 1;
		}
		if (Tools_Wii_Export::GetHierarchyExportUV1())
		{
			uSizePerVert += 2; // GXTexCoord1x16
		}
		return uSizePerVert;
	};
	// what vertex format does this display list use
	virtual u_char StripType()
	{
		switch (Tools_Wii_Export::GetHierarchyExportFlags()&(ASURA_WII_HIERARCHY_CHUNK_FLAG_NORMALS|ASURA_WII_HIERARCHY_CHUNK_FLAG_UV1|ASURA_WII_HIERARCHY_CHUNK_FLAG_COLOURS))
		{
			case (ASURA_WII_HIERARCHY_CHUNK_FLAG_NORMALS|ASURA_WII_HIERARCHY_CHUNK_FLAG_UV1): { return 1; }// GX_VTXFMT1;
			case (ASURA_WII_HIERARCHY_CHUNK_FLAG_COLOURS|ASURA_WII_HIERARCHY_CHUNK_FLAG_UV1): { return 6; }// GX_VTXFMT6;
			default: { Asura_Assert(0, "Unsupported Hierarchy export flags"); return 1; }// GX_VTXFMT1;
		}
	}
	// store one vertex worth of data
	virtual void StoreVert(void *&pV, u_int uIndex)
	{
		const u_short usIndex = static_cast<u_short>(uIndex);
		Store(pV, usIndex); // GXPosition1x16(m_pusIndices[uIndex])

		if (Tools_Wii_Export::GetHierarchyExportNormals())
		{
			Store(pV, usIndex); // GXNormal1x16(m_pusIndices[uIndex]);
		}
		if (Tools_Wii_Export::GetHierarchyExportColours())
		{
			Store(pV, usIndex); // GXColor1x16(m_pusIndices[uIndex]);
		}
		if(Tools_Wii_Export::GetHierarchyExportAlpha())
		{
			const Tools_HierarchyProcessor_MultiMaterial_Strip* pxStrip = Tools_HierarchyProcessor_MultiMaterial::GetCattedStrip();
			const u_char ucAlpha = static_cast<u_char>(pxStrip->m_pxVertices[uIndex].m_fAlpha * 0xff);
			Store(pV, ucAlpha);
		}
		if (Tools_Wii_Export::GetHierarchyExportUV1())
		{
			Store(pV, usIndex); // GXTexCoord1x16(m_pusIndices[uIndex]);
		}
	}
private:
	ToolsSimp_WiiHierarchyDisplayLists(const ToolsSimp_WiiHierarchyDisplayLists&) {}
	const Tools_EnvironmentProcessor_Mesh* m_pxProcessedMesh;
};
static ToolsSimp_WiiHierarchyDisplayLists s_xWiiHierarchyDisplayListGen;

bool Tools_Wii_Export::ExportSmoothSkin( HANDLE hOutput, Asura_Hierarchy_Skin* pxSkin)
{
	Asura_Assert( pxSkin, "NULL skin pointer passed to Tools_Wii_Export::ExportSmoothSkin()." );

	Tools_SmoothSkin_Processor xProcessor;
	if( !xProcessor.Initialise( pxSkin ) ) { return false; }

	Tools_SmoothSkin_Wii_Processor xWiiProcessor;
	if( !xWiiProcessor.OutputToChunk( &xProcessor, hOutput ) ) { return false; }

	return true;
}

template <typename T> inline T ConvertToFixedPoint(const u_int uPrecision, const float fVal, const char* szValueDescription)
{
	const float fMultiplier = static_cast<float>(1 << uPrecision);
#ifdef ASURA_ASSERT
	const float fMaxValue = static_cast<float>(1 << (8 * sizeof(T) - uPrecision - 1));
	Asura_Assertf(fVal > -fMaxValue && fVal <= fMaxValue, ("%s out of range in mesh %s (value %f, maximum size %f)", szValueDescription, g_szCurrentMeshName, fVal, fMaxValue) );
#endif
	return (T)(fVal * fMultiplier);
}

const inline short ConvertHierarchyPosition(float fValue)
{
	// Position is stored as shorts with 10 fractional bits.
	return ConvertToFixedPoint<short>(g_uHierarchyFractionalBits, fValue, "Position");
}

const inline short ConvertNormal(float fValue)
{
	// Normals are stored as shorts with 14 fractional bits.
	return ConvertToFixedPoint<short>(14, fValue, "Normal");
}

const inline short ConvertUV(float fValue)
{
	// Texture co-ordinates are stored as shorts with 10 fractional bits.
	return ConvertToFixedPoint<short>(10, fValue, "UV");
}

bool Tools_Wii_Export::ExportHierarchy(HANDLE hOutput, class Asura_Hierarchy_Skin* pxSkin)
{
	Asura_Assert( pxSkin, "NULL skin pointer passed to Tools_Wii_Export::ExportHierarchy()." );
	Asura_Assert( GetHierarchyExportDisplayList(), "Only display list export format supported");

	// Hierarchy
	if(!Tools_HierarchyProcessor_MultiMaterial::Initialise(pxSkin))
	{
		return false;
	}

	SetHierarchyExportAlpha(Tools_HierarchyProcessor_MultiMaterial::DoesRequireVertexAlpha());

	Tools_HierarchyProcessor_MultiMaterial::ConvertMaterialsForExport();

	const Tools_HierarchyProcessor_MultiMaterial_Strip* pxCattedStrip = Tools_HierarchyProcessor_MultiMaterial::GetCattedStrip();
	if(!pxCattedStrip)
	{
		Tools_HierarchyProcessor_MultiMaterial::Shutdown();
		return false;
	}

	// Calculate min fractional bits necessary for strip
	g_uHierarchyFractionalBits = 15;
	for(unsigned int uVertex = 0; uVertex < pxCattedStrip->m_uNumberOfVertices; ++uVertex)
	{
		const Tools_HierarchyProcessor_MultiMaterial_Vertex* pxVertex = pxCattedStrip->m_pxVertices + uVertex;
		const float fAbsX = Asura_Maths::Abs(pxVertex->m_xPosition.x);
		const float fAbsY = Asura_Maths::Abs(pxVertex->m_xPosition.y);
		const float fAbsZ = Asura_Maths::Abs(pxVertex->m_xPosition.z);
		const float fMaxValue = Asura_Maths::Max(Asura_Maths::Max(fAbsX, fAbsY), fAbsZ);
		// Nudge the value up a fraction to ensure that we get enough bits.
		const float fNonFractionalBits = logf(fMaxValue + .5f) / logf(2.f);
		const u_int uFractionalBits = 14 - Asura_Maths::FloatToInt_Chop(fNonFractionalBits);
		g_uHierarchyFractionalBits = Asura_Maths::Min(g_uHierarchyFractionalBits, uFractionalBits);
	}
	const u_int uNumberOfStrips = Tools_HierarchyProcessor_MultiMaterial::GetTotalNumberOfStrips();
	const u_int uNumberOfVertices = Tools_HierarchyProcessor_MultiMaterial::GetTotalNumberOfVertices();

	Asura_Chunk_Memory_Stream xStream;

	Asura_String xName = "Stripped";
	xName += pxSkin->Name;

	xStream.WriteResourceChunk_Start(ASURA_RESOURCEFILE_TYPE_PLATFORMSPECIFIC, ASURA_RESOURCEFILE_TYPE_WII_HIERARCHY, ASURA_WII_HIERARCHY_CHUNK_VERSION, 0, xName);

	// 	struct 
	// 	{
	// 		u_int uHash; 
	// 		u_int uFlags;
	// 		u_int uNumberOfStrips; 
	// 		u_int uDisplayListSize;
	// 		u_int uNumberOfVertices;
	//		struct 
	//      {
	//        int iMatID;
	//        u_int uDisplayListSize;
	//      } StripInfos[uNumberOfStrips]
	//      struct 
	//      {
	//        short sPos[3];		// 6.10 format
	//		  short sNormal[3];		// 2.14 format		// if (uFlags & ExportHierarchyNormals)
	//        // 4 byte alignment enforced!
	//		  u_int  uColour;							// if (uFlags & ExportHierarchyColours)
	//        short sUV[2];			// 6.10 format		// if (uFlags & ExportHierarchyUV1)
	//		} Vertices[uNumberOfVertices]
	//  }

	g_szCurrentMeshName = pxSkin->Name.GetString();
	const Asura_Hash_ID uHashID = Asura_GetHashID(g_szCurrentMeshName);
	xStream << uHashID;
	
	void **pvDisplayLists = NULL;;
	u_int *puDisplayListSizes = NULL;
	u_int uTotalDisplayListSize = 0;
	u_int *puTriCounts = NULL;

	if (!s_pxDisplayListGenerator)
	{
		s_pxDisplayListGenerator = &s_xWiiHierarchyDisplayListGen;
	}
	pvDisplayLists = new void*[uNumberOfStrips];
	puDisplayListSizes = new u_int[uNumberOfStrips];
	puTriCounts = new u_int[uNumberOfStrips];
	u_int uIndex = 0;
	for(u_int uStrip = 0; uStrip < uNumberOfStrips; ++uStrip)
	{
		puTriCounts[uStrip] = s_pxDisplayListGenerator->CreateDisplayList(pvDisplayLists[uStrip], puDisplayListSizes[uStrip], 
			pxCattedStrip->m_pusIndices + uIndex, Tools_HierarchyProcessor_MultiMaterial::GetProcessedStrip(uStrip)->m_uNumberOfIndices);
		uIndex += Tools_HierarchyProcessor_MultiMaterial::GetProcessedStrip(uStrip)->m_uNumberOfIndices;
		uTotalDisplayListSize += puDisplayListSizes[uStrip];
	}

	xStream << s_uHierarchyExportFlags;
	xStream << uNumberOfStrips;
	xStream << uTotalDisplayListSize;
	xStream << uNumberOfVertices;
	xStream << g_uHierarchyFractionalBits;

	for(u_int uStrip = 0; uStrip < uNumberOfStrips; ++uStrip)
	{
		const Tools_HierarchyProcessor_MultiMaterial_Strip* pxStrip = Tools_HierarchyProcessor_MultiMaterial::GetProcessedStrip(uStrip);
		xStream << pxStrip->m_iMaterialID;
		xStream << pxStrip->m_usBoneIndex;
		xStream << puDisplayListSizes[uStrip];
		xStream << puTriCounts[uStrip];
	}
	for(u_int uVertex = 0; uVertex < pxCattedStrip->m_uNumberOfVertices; ++uVertex)
	{
		const Tools_HierarchyProcessor_MultiMaterial_Vertex* pxVertex = pxCattedStrip->m_pxVertices + uVertex;
		u_int uOffset = 0;

		const Asura_Vector_3& xPos = pxVertex->m_xPosition;
		xStream << ConvertHierarchyPosition(xPos.x);
		xStream << ConvertHierarchyPosition(xPos.y);
		xStream << ConvertHierarchyPosition(xPos.z);

		uOffset += 3 * sizeof(short);

		if (GetHierarchyExportNormals())
		{
			const Asura_Vector_3& xNormal = pxVertex->m_xNormal;
			xStream << ConvertNormal(xNormal.x);
			xStream << ConvertNormal(xNormal.y);
			xStream << ConvertNormal(xNormal.z);
			uOffset += 3 * sizeof(short);
		}
		if (GetHierarchyExportColours())
		{
			while ((uOffset & 3) != 0) // do we need to pad the int ?
			{
				xStream << static_cast<char>(0);
				++uOffset;
			}

			if (s_HierarchyColorFunc)
			{
				xStream << static_cast<unsigned int>(s_HierarchyColorFunc(pxVertex));
			}
			else
			{
				xStream << static_cast<unsigned int>(pxVertex->m_uARGBColour);
			}
		}
		if (GetHierarchyExportUV1())
		{
			xStream << ConvertUV(pxVertex->m_fU0);
			xStream << ConvertUV(pxVertex->m_fV0);
		}
	}

	for(u_int uStrip = 0; uStrip < uNumberOfStrips; ++uStrip)
	{
		xStream.Write(puDisplayListSizes[uStrip], pvDisplayLists[uStrip]);
		delete[] (char*)pvDisplayLists[uStrip];
	}
	delete[] pvDisplayLists;
	delete[] puDisplayListSizes;
	delete[] puTriCounts;

	Tools_HierarchyProcessor_MultiMaterial::Shutdown();

	xStream.WriteResourceChunk_End();
	const void* pData = xStream.GetStartAddress();
	u_int uChunkSize = xStream.GetPosition();

	unsigned long uBytesWritten = 0;
	// Write the data to the file...
	WriteFile(hOutput, pData, uChunkSize, &uBytesWritten, NULL);

	Asura_Assert(uChunkSize == uBytesWritten, "Did not write out expected number of bytes");

	return true;
}

class Tools_WiiEnvironmentDisplayLists : public Tools_Wii_DisplayListFactory
{
private:
	const Tools_EnvironmentProcessor_PosMesh* m_pxPositionMesh;
	const Tools_EnvironmentProcessor_Mesh* m_pxProcessedMesh;
	bool m_bIncludeNormals;
	bool m_bIsDualPass;
	u_int m_uBaseIndex;
	u_int m_uStrip;
	Tools_WiiEnvironmentDisplayLists (const Tools_WiiEnvironmentDisplayLists&) {}
protected:
	virtual u_int SizePerVert()
	{
		const u_int uPOSITIONSIZE = 2;
		const u_int uNORMALSIZE = m_bIncludeNormals ? 1 : 0;
		const u_int uCOLOURSIZE = 3;
		const u_int uTEXCOORDSIZE = 2;
		const u_int uTEXCOORDSIZE2 = m_bIsDualPass ? 2 : 0;
		return (uPOSITIONSIZE + uNORMALSIZE + uCOLOURSIZE + uTEXCOORDSIZE + uTEXCOORDSIZE2);
	};
	// what vertex format does this display list use
	virtual u_char StripType()
	{
		return 0; //GX_VTXFMT0
	}
	// store one vertex worth of data
	virtual void StoreVert(void *&pV, u_int uIndex)
	{
		const Tools_EnvironmentProcessor_Vertex& xVertex = m_pxProcessedMesh->m_pxStrips[m_uStrip].m_pxVertices[uIndex];
		// Position
		Store(pV, m_pxPositionMesh->m_ppusPositionMap[m_uStrip][uIndex]);
		// Normal
		if (m_bIncludeNormals)
		{
			Store(pV, Tools_Wii_Export::GetClosestNormalIndex(xVertex.m_xNormal));
		}
		// Colour
		ENVIRONMENT_ENCODE_COLOUR_FN pfnEncodeFn = Tools_Wii_Export::GetEnvEncodeFunction();
		const u_int uColour = pfnEncodeFn(xVertex.m_uDiffuseRGBA);
		// Slightly nasty hackery to endian swap a 3-byte colour
		Store(pV, *(reinterpret_cast<const char*>(&uColour) + 2));
		Store(pV, *(reinterpret_cast<const char*>(&uColour) + 1));
		Store(pV, *(reinterpret_cast<const char*>(&uColour) + 0));
		if(m_bIsDualPass)
		{
			// UV * 2
			Store(pV, static_cast<u_short>(2 * uIndex + m_uBaseIndex));
			Store(pV, static_cast<u_short>(2 * uIndex + m_uBaseIndex + 1));
		}
		else
		{
			// UV
			Store(pV, static_cast<u_short>(uIndex + m_uBaseIndex));
		}
	}

public:
	Tools_WiiEnvironmentDisplayLists (u_int uModule, bool bIncludeNormals)
	: m_pxPositionMesh(Tools_EnvironmentProcessor::GetPositionMesh(uModule))
	, m_pxProcessedMesh(Tools_EnvironmentProcessor::GetProcessedMesh(uModule))
	, m_bIncludeNormals(bIncludeNormals)
	, m_bIsDualPass(false)
	, m_uBaseIndex(0)
	{
	}
	
	void StartStrip(u_int uStrip)
	{
		m_uStrip = uStrip;
		const int iMaterialID = m_pxProcessedMesh->m_pxStrips[m_uStrip].m_iUnalteredMaterialID;
		m_bIsDualPass = Asura_Material_System::IsMaterialDualTexture(iMaterialID);
	}
	void EndStrip()
	{
		m_uBaseIndex += m_pxProcessedMesh->m_pxStrips[m_uStrip].m_uNumberOfVertices * (m_bIsDualPass ? 2 : 1);
	}
};

unsigned char Tools_Wii_Export::GetClosestNormalIndex(const Asura_Vector_3& xNormal)
{
	u_char ucClosest = 0;
	float fClosest = -1.f;
	for(u_char uc = 0; uc < s_uNormalTableNumNormals; ++uc)
	{
		const float fDP = s_axNormalTable[uc] * xNormal;
		if(fDP > fClosest)
		{
			ucClosest = uc;
			fClosest = fDP;
		}
	}
	return ucClosest;
}

u_int Tools_Wii_Export::ARGB8888ToRGBA6(u_int uARGB)
{
	const u_char ucA = (unsigned char)((uARGB & 0xff000000) >> (32 - 6));
	const u_char ucR = (unsigned char)((uARGB & 0x00ff0000) >> (24 - 6));
	const u_char ucG = (unsigned char)((uARGB & 0x0000ff00) >> (16 - 6));
	const u_char ucB = (unsigned char)((uARGB & 0x000000ff) >> ( 8 - 6));
	return (ucR << (6 * 3)) | (ucG << (6 * 2)) | (ucB << (6 * 1)) | (ucA);
}

void Tools_Wii_Export::CalculateEnvironmentDisplayLists(Tools_Wii_Export_Processed_Module* pxProcessedModules, unsigned int &uTotalDisplayListData, bool bIncludeNormals)
{
	const u_int uNumberOfModules = Asura_Module_System::GetNumberOfModules();

	for(u_int uModule = 0; uModule < uNumberOfModules; uModule++)
	{
		const Tools_EnvironmentProcessor_Mesh* pxProcessedMesh = Tools_EnvironmentProcessor::GetProcessedMesh(uModule);
		Tools_Wii_Export_Processed_Module* pxProcessedModule = &pxProcessedModules[uModule];

		pxProcessedModule->m_ppcDisplayListData = new char*[pxProcessedMesh->m_uNumberOfStrips];
		pxProcessedModule->m_puDisplayListSizes = new u_int[pxProcessedMesh->m_uNumberOfStrips];

		Tools_WiiEnvironmentDisplayLists xDisplayListGen(uModule, bIncludeNormals);

		// Save strips
		Tools_EnvironmentProcessor_Strip* pStrips = pxProcessedMesh->m_pxStrips;
		for(u_int uStrip = 0; uStrip < pxProcessedMesh->m_uNumberOfStrips; uStrip++, pStrips++)
		{
			xDisplayListGen.StartStrip(uStrip);
			void *pDisplayList;
			u_int uDisplayListSize;
			u_int uNumTris = xDisplayListGen.CreateDisplayList(pDisplayList, uDisplayListSize, pStrips->m_pusIndices, pStrips->m_uNumberOfIndices);
			pxProcessedModule->m_ppcDisplayListData[uStrip] = (char*)pDisplayList;
			pxProcessedModule->m_puDisplayListSizes[uStrip] = uDisplayListSize;

			uTotalDisplayListData += uDisplayListSize;
			// Fake this so triangle count displayed at runtime works correctly
			pStrips->m_uNumberOfIndices = uNumTris + 2;
			xDisplayListGen.EndStrip();
		}
	}
}

void Tools_Wii_Export::WriteBoundingBox(Asura_Chunk_Stream& xStream, const Asura_Vector_3 *pPositions, u_int uNumPositions)
{
	// Calculate bounding box.
	Asura_Bounding_Box xBB;
	xBB.Reset();
	for(; uNumPositions; uNumPositions--, pPositions++)
	{
		xBB.Set(*pPositions);
	}
	// Save bounding box
	xStream << xBB.MinX;
	xStream << xBB.MaxX;
	xStream << xBB.MinY;
	xStream << xBB.MaxY;
	xStream << xBB.MinZ;
	xStream << xBB.MaxZ;
}

void Tools_Wii_Export::ExportEnvironment(HANDLE hOutput, bool bSubdivide)
{
	const u_int uNumberOfModules = Asura_Module_System::GetNumberOfModules();
	// For file output
	Asura_String xResourceFilename;
	const Tools_EnvironmentProcessor_Mesh* pxProcessedMesh;
	const Tools_EnvironmentProcessor_PosMesh* pxPositionMesh;
	Asura_Chunk_ResourceFile xResourceFile;

	Tools_EnvironmentProcessor::Initialise(true, true, bSubdivide);

	Tools_EnvironmentProcessor::GroupByPosition();

	const unsigned int uTotalPositions = Tools_EnvironmentProcessor::GetTotalNumberOfPositions();
	const unsigned int uTotalVertices = Tools_EnvironmentProcessor::GetTotalNumberOfVertices();

	Tools_Wii_Export_Processed_Module* pxProcessedModules = new Tools_Wii_Export_Processed_Module[uNumberOfModules];
	u_int uTotalTexcoords = 0;
	for(u_int uModule = 0; uModule < uNumberOfModules; ++uModule)
	{
		pxProcessedMesh = Tools_EnvironmentProcessor::GetProcessedMesh(uModule);
		pxProcessedModules[uModule].m_uNumTexcoords = 0;
		for(u_int uStrip = 0; uStrip < pxProcessedMesh->m_uNumberOfStrips; ++uStrip)
		{
			const Tools_EnvironmentProcessor_Strip* pxStrip = pxProcessedMesh->m_pxStrips + uStrip;
			const bool bIsDualPass = Asura_Material_System::IsMaterialDualTexture(pxStrip->m_iUnalteredMaterialID);
			const u_int uNumTexcoords = pxStrip->m_uNumberOfVertices * (bIsDualPass ? 2 : 1);
			pxProcessedModules[uModule].m_uNumTexcoords += uNumTexcoords;
			uTotalTexcoords += uNumTexcoords;
		}
	}

	xResourceFilename = "StrippedEnv";

	unsigned int uTotalDisplayListData = 0;

	Asura_Assert(GetEnvironmentExportFlag(EXPORT_FLAG_USE_DISPLAY_LISTS), "Non display-list environment export deprecated.");
	CalculateEnvironmentDisplayLists(pxProcessedModules, uTotalDisplayListData, GetEnvironmentExportFlag(EXPORT_FLAG_NORMALS));

	Asura_Chunk_Memory_Stream xStream;
	xStream.WriteResourceChunk_Start(ASURA_RESOURCEFILE_TYPE_PLATFORMSPECIFIC, ASURA_RESOURCEFILE_TYPE_WII_STRIPPEDENV, 0, 0, xResourceFilename);

	// 	struct 
	// 	{
	// 		u_int uVersion; // = 1
	const int iChunkVersion = 1;
	xStream << iChunkVersion;
	// 		u_int uNumModules;
	xStream << uNumberOfModules;
	// 		u_int uFlags;
	xStream << s_uEnvironmentExportFlags;
	// 		u_int uNumPositions; //= sum(Modules.uNumPositions);
	xStream << uTotalPositions;
	// 		u_int uNumVerticies; //= sum(Modules.uNumVerticies);
	xStream << uTotalVertices;
	xStream << uTotalTexcoords;
	// 		u_int uNumNormals;  // if (uFlags & EXPORT_FLAG_NORMALS)
	if (GetEnvironmentExportFlag(EXPORT_FLAG_NORMALS))
	{
		xStream << s_uNormalTableNumNormals;
	}
	// 		u_int uTotalDisplayListSize;  // if (uFlags & EXPORT_FLAG_USE_DISPLAY_LISTS)
	if (GetEnvironmentExportFlag(EXPORT_FLAG_USE_DISPLAY_LISTS))
	{
		xStream << uTotalDisplayListData;
	}
	// 		float fNormals[uNumNormals * 3]; // if (uFlags & EXPORT_FLAG_NORMALS)
	if (GetEnvironmentExportFlag(EXPORT_FLAG_NORMALS))
	{
		for(unsigned int u = 0; u < s_uNormalTableNumNormals; u++)
		{
			xStream << s_axNormalTable[u].x;
			xStream << s_axNormalTable[u].y;
			xStream << s_axNormalTable[u].z;
		}
	}
	// 		struct  
	// 		{
	u_int uTexcoordsWritten = 0;
	for(u_int uModule = 0; uModule < uNumberOfModules; ++uModule)
	{
		Tools_Level_Export::UpdateStatus(Tools_Level_Export::EXPORT_STAGE_SAVING_ENVIRONMENT_TO_FILE, "Saving environment to file", uModule * 100 / uNumberOfModules);
		pxProcessedMesh = Tools_EnvironmentProcessor::GetProcessedMesh(uModule);
		pxPositionMesh = Tools_EnvironmentProcessor::GetPositionMesh(uModule);
		g_szCurrentMeshName = Asura_Module_System::GetMeshForModule(uModule)->Name.GetString();
		Tools_Wii_Export_Processed_Module* pxProcessedModule = &pxProcessedModules[uModule];

		// 			u_int uNumPositions;
		xStream << pxPositionMesh->m_uNumberOfPositions;
		// 			u_int uNumVerticies;
		xStream << pxProcessedModule->m_uNumTexcoords;
		// 			u_int uNumStrips;
		xStream << pxProcessedMesh->m_uNumberOfStrips;
		// 			Asura_Bounding_Box xBB;
		WriteBoundingBox(xStream, pxPositionMesh->m_pxPositions, pxPositionMesh->m_uNumberOfPositions);

		// 			float fPositions[uNumPositions * 3];
		for(u_int uVertex = 0; uVertex < pxPositionMesh->m_uNumberOfPositions; ++uVertex)
		{
			xStream << pxPositionMesh->m_pxPositions[uVertex].x;
			xStream << pxPositionMesh->m_pxPositions[uVertex].y;
			xStream << pxPositionMesh->m_pxPositions[uVertex].z;
		}

		// 			short sUV[uNumVerticies*2]; // 6.10 format
		for(u_int uStrip = 0; uStrip < pxProcessedMesh->m_uNumberOfStrips; ++uStrip)
		{
			const Tools_EnvironmentProcessor_Strip* pxStrip = pxProcessedMesh->m_pxStrips + uStrip;
			const bool bIsDualPass = Asura_Material_System::IsMaterialDualTexture(pxStrip->m_iUnalteredMaterialID);

			for(u_int uVertex = 0; uVertex < pxStrip->m_uNumberOfVertices; ++uVertex)
			{
				const Tools_EnvironmentProcessor_Vertex* pxVertex = pxStrip->m_pxVertices + uVertex;
				xStream << ConvertUV(pxVertex->m_fU);
				xStream << ConvertUV(pxVertex->m_fV);
				++uTexcoordsWritten;
				if(bIsDualPass)
				{
					xStream << ConvertUV(pxVertex->m_fU2);
					xStream << ConvertUV(pxVertex->m_fV2);
					++uTexcoordsWritten;
				}
			}
		}

		// 			struct {
		for(u_int uStrip = 0; uStrip < pxProcessedMesh->m_uNumberOfStrips; ++uStrip)
		{
			const Tools_EnvironmentProcessor_Strip* pxStrip = pxProcessedMesh->m_pxStrips + uStrip;

			// 				u_int uStripFlags;
			xStream << pxStrip->m_uPolygonFlags;
			// 				u_int uMatID;
			xStream << pxStrip->m_iMaterialID;
			// 				u_int uNumIndicies;
			xStream << pxStrip->m_uNumberOfIndices;
			if (GetEnvironmentExportFlag(EXPORT_FLAG_USE_DISPLAY_LISTS))
			{
				// 				u_int uDisplayListSize; // if (uFlags & EXPORT_FLAG_USE_DISPLAY_LISTS)
				xStream << pxProcessedModule->m_puDisplayListSizes[uStrip];
				// 			    char cDisplayList[uDisplayListSize] // if (uFlags & EXPORT_FLAG_USE_DISPLAY_LISTS)
				xStream.Write(pxProcessedModule->m_puDisplayListSizes[uStrip], pxProcessedModule->m_ppcDisplayListData[uStrip]);
			}
			// 			} Strips [uNumStrips];
		}
		// 		} Modules[uNumModules];
	}
	// 	};
	Asura_Assert(uTexcoordsWritten == uTotalTexcoords, "Mismatch in texcoords written");

	// Clean up
	for(u_int uModule = 0; uModule < uNumberOfModules; ++uModule)
	{
		pxProcessedMesh = Tools_EnvironmentProcessor::GetProcessedMesh(uModule);
		Tools_Wii_Export_Processed_Module* pxProcessedModule = &pxProcessedModules[uModule];
		if (pxProcessedModule->m_ppcDisplayListData)
		{
			for(u_int uStrip = 0; uStrip < pxProcessedMesh->m_uNumberOfStrips; ++uStrip)
			{
				delete [] pxProcessedModule->m_ppcDisplayListData[uStrip];
			}
			delete [] pxProcessedModule->m_ppcDisplayListData;
		}
		delete [] pxProcessedModule->m_puDisplayListSizes;
	}
	delete [] pxProcessedModules;

	Tools_EnvironmentProcessor::Shutdown();

	xStream.WriteResourceChunk_End();
	const void* pData = xStream.GetStartAddress();
	u_int uChunkSize = xStream.GetPosition();

	unsigned long uBytesWritten = 0;
	// Write the data to the file...
	WriteFile(hOutput, pData, uChunkSize, &uBytesWritten, NULL);

	Asura_Assert(uChunkSize == uBytesWritten, "Did not write out expected number of bytes");
}

bool Tools_Wii_Export::ExportShape(HANDLE hOutput, Asura_Mesh* pxMesh, Asura_Hash_ID uHashID, int iMaterialIndex)
{
	if(!pxMesh)
	{
		return false;
	}
	Asura_String xResourceFilename("Stripped");
	xResourceFilename += pxMesh->Name;

	Tools_ObjectProcessor::Initialise(pxMesh,iMaterialIndex);

	const Tools_ObjectProcessor_Strip* pxStrip = Tools_ObjectProcessor::GetProcessedStrip();
	if(!pxStrip)
	{
		Tools_ObjectProcessor::Shutdown();
		return false;
	}


	Asura_Chunk_Memory_Stream xStream;
	xStream.WriteResourceChunk_Start(ASURA_RESOURCEFILE_TYPE_PLATFORMSPECIFIC, ASURA_RESOURCEFILE_TYPE_WII_SHAPE, 0, 0, xResourceFilename);

	// Now passed in
	xStream << uHashID;
	xStream << pxStrip->m_iMaterialID;
	xStream << pxStrip->m_uNumberOfIndices;
	xStream << pxStrip->m_uNumberOfVertices;

	xStream << pxStrip->m_xBBMax.x;
	xStream << pxStrip->m_xBBMax.y;
	xStream << pxStrip->m_xBBMax.z;
	xStream << pxStrip->m_xBBMin.x;
	xStream << pxStrip->m_xBBMin.y;
	xStream << pxStrip->m_xBBMin.z;
	for(unsigned int uVertex = 0; uVertex < pxStrip->m_uNumberOfVertices; uVertex++)
	{
		Tools_ObjectProcessor_Vertex* pxVertex = pxStrip->m_pxVertices + uVertex;
		xStream << pxVertex->m_fX;
		xStream << pxVertex->m_fY;
		xStream << pxVertex->m_fZ;
		xStream << pxVertex->m_fNX;
		xStream << pxVertex->m_fNY;
		xStream << pxVertex->m_fNZ;
		xStream << pxVertex->m_fU;
		xStream << pxVertex->m_fV;
	}
	for(unsigned int uIndex = 0; uIndex < pxStrip->m_uNumberOfIndices; uIndex++)
	{
		xStream << pxStrip->m_pusIndices[uIndex];
	}

	Tools_ObjectProcessor::Shutdown();

	xStream.WriteResourceChunk_End();
	const void* pData = xStream.GetStartAddress();
	u_int uChunkSize = xStream.GetPosition();

	unsigned long uBytesWritten = 0;
	// Write the data to the file...
	WriteFile(hOutput, pData, uChunkSize, &uBytesWritten, NULL);

	Asura_Assert(uChunkSize == uBytesWritten, "Did not write out expected number of bytes");

	return true;
}

unsigned int Tools_Wii_Export::s_uNormalTableNumNormals = 252;
Asura_Vector_3 Tools_Wii_Export::s_axNormalTable[252] =
{
	Asura_Vector_3(0.000000000000000F, 0.000000000000000F, 1.000000000000000F),
	Asura_Vector_3(0.219604492187500F, 0.000000000000000F, 0.975524902343750F),
	Asura_Vector_3(0.067810058593750F, 0.208862304687500F, 0.975524902343750F),
	Asura_Vector_3(0.428466796875000F, 0.000000000000000F, 0.903503417968750F),
	Asura_Vector_3(0.289794921875000F, 0.210510253906250F, 0.933593750000000F),
	Asura_Vector_3(0.132385253906250F, 0.407531738281250F, 0.903503417968750F),
	Asura_Vector_3(0.616455078125000F, 0.000000000000000F, 0.787353515625000F),
	Asura_Vector_3(0.502075195312500F, 0.212341308593750F, 0.838317871093750F),
	Asura_Vector_3(0.357116699218750F, 0.411865234375000F, 0.838317871093750F),
	Asura_Vector_3(0.190490722656250F, 0.586303710937500F, 0.787353515625000F),
	Asura_Vector_3(0.774353027343750F, 0.000000000000000F, 0.632690429687500F),
	Asura_Vector_3(0.690979003906250F, 0.212646484375000F, 0.690856933593750F),
	Asura_Vector_3(0.569152832031250F, 0.413513183593750F, 0.710571289062500F),
	Asura_Vector_3(0.415771484375000F, 0.591430664062500F, 0.690856933593750F),
	Asura_Vector_3(0.239257812500000F, 0.736450195312500F, 0.632690429687500F),
	Asura_Vector_3(0.894409179687500F, 0.000000000000000F, 0.447204589843750F),
	Asura_Vector_3(0.842224121093750F, 0.208862304687500F, 0.496948242187500F),
	Asura_Vector_3(0.748901367187500F, 0.407531738281250F, 0.522460937500000F),
	Asura_Vector_3(0.619018554687500F, 0.586303710937500F, 0.522460937500000F),
	Asura_Vector_3(0.458862304687500F, 0.736450195312500F, 0.496948242187500F),
	Asura_Vector_3(0.276367187500000F, 0.850646972656250F, 0.447204589843750F),
	Asura_Vector_3(-0.177673339843750F, 0.129089355468750F, 0.975524902343750F),
	Asura_Vector_3(-0.110656738281250F, 0.340698242187500F, 0.933593750000000F),
	Asura_Vector_3(-0.346679687500000F, 0.251831054687500F, 0.903503417968750F),
	Asura_Vector_3(-0.046813964843750F, 0.543151855468750F, 0.838317871093750F),
	Asura_Vector_3(-0.281311035156250F, 0.466918945312500F, 0.838317871093750F),
	Asura_Vector_3(-0.498718261718750F, 0.362365722656250F, 0.787353515625000F),
	Asura_Vector_3(0.011230468750000F, 0.722839355468750F, 0.690856933593750F),
	Asura_Vector_3(-0.217407226562500F, 0.669128417968750F, 0.710571289062500F),
	Asura_Vector_3(-0.433959960937500F, 0.578186035156250F, 0.690856933593750F),
	Asura_Vector_3(-0.626464843750000F, 0.455139160156250F, 0.632690429687500F),
	Asura_Vector_3(0.061584472656250F, 0.865539550781250F, 0.496948242187500F),
	Asura_Vector_3(-0.156127929687500F, 0.838195800781250F, 0.522460937500000F),
	Asura_Vector_3(-0.366333007812500F, 0.769897460937500F, 0.522460937500000F),
	Asura_Vector_3(-0.558593750000000F, 0.664001464843750F, 0.496948242187500F),
	Asura_Vector_3(-0.723571777343750F, 0.525695800781250F, 0.447204589843750F),
	Asura_Vector_3(-0.177673339843750F, -0.129089355468750F, 0.975524902343750F),
	Asura_Vector_3(-0.358215332031250F, 0.000000000000000F, 0.933593750000000F),
	Asura_Vector_3(-0.346679687500000F, -0.251831054687500F, 0.903503417968750F),
	Asura_Vector_3(-0.531005859375000F, 0.123229980468750F, 0.838317871093750F),
	Asura_Vector_3(-0.531005859375000F, -0.123229980468750F, 0.838317871093750F),
	Asura_Vector_3(-0.498718261718750F, -0.362365722656250F, 0.787353515625000F),
	Asura_Vector_3(-0.684020996093750F, 0.234069824218750F, 0.690856933593750F),
	Asura_Vector_3(-0.703552246093750F, 0.000000000000000F, 0.710571289062500F),
	Asura_Vector_3(-0.684020996093750F, -0.234069824218750F, 0.690856933593750F),
	Asura_Vector_3(-0.626464843750000F, -0.455139160156250F, 0.632690429687500F),
	Asura_Vector_3(-0.804138183593750F, 0.326049804687500F, 0.496948242187500F),
	Asura_Vector_3(-0.845397949218750F, 0.110473632812500F, 0.522460937500000F),
	Asura_Vector_3(-0.845397949218750F, -0.110473632812500F, 0.522460937500000F),
	Asura_Vector_3(-0.804138183593750F, -0.326049804687500F, 0.496948242187500F),
	Asura_Vector_3(-0.723571777343750F, -0.525695800781250F, 0.447204589843750F),
	Asura_Vector_3(0.067810058593750F, -0.208862304687500F, 0.975524902343750F),
	Asura_Vector_3(-0.110656738281250F, -0.340698242187500F, 0.933593750000000F),
	Asura_Vector_3(0.132385253906250F, -0.407531738281250F, 0.903503417968750F),
	Asura_Vector_3(-0.281311035156250F, -0.466918945312500F, 0.838317871093750F),
	Asura_Vector_3(-0.046813964843750F, -0.543151855468750F, 0.838317871093750F),
	Asura_Vector_3(0.190490722656250F, -0.586303710937500F, 0.787353515625000F),
	Asura_Vector_3(-0.433959960937500F, -0.578186035156250F, 0.690856933593750F),
	Asura_Vector_3(-0.217407226562500F, -0.669128417968750F, 0.710571289062500F),
	Asura_Vector_3(0.011230468750000F, -0.722839355468750F, 0.690856933593750F),
	Asura_Vector_3(0.239257812500000F, -0.736450195312500F, 0.632690429687500F),
	Asura_Vector_3(-0.558593750000000F, -0.664001464843750F, 0.496948242187500F),
	Asura_Vector_3(-0.366333007812500F, -0.769897460937500F, 0.522460937500000F),
	Asura_Vector_3(-0.156127929687500F, -0.838195800781250F, 0.522460937500000F),
	Asura_Vector_3(0.061584472656250F, -0.865539550781250F, 0.496948242187500F),
	Asura_Vector_3(0.276367187500000F, -0.850646972656250F, 0.447204589843750F),
	Asura_Vector_3(0.289794921875000F, -0.210510253906250F, 0.933593750000000F),
	Asura_Vector_3(0.357116699218750F, -0.411865234375000F, 0.838317871093750F),
	Asura_Vector_3(0.502075195312500F, -0.212341308593750F, 0.838317871093750F),
	Asura_Vector_3(0.415771484375000F, -0.591430664062500F, 0.690856933593750F),
	Asura_Vector_3(0.569152832031250F, -0.413513183593750F, 0.710571289062500F),
	Asura_Vector_3(0.690979003906250F, -0.212646484375000F, 0.690856933593750F),
	Asura_Vector_3(0.458862304687500F, -0.736450195312500F, 0.496948242187500F),
	Asura_Vector_3(0.619018554687500F, -0.586303710937500F, 0.522460937500000F),
	Asura_Vector_3(0.748901367187500F, -0.407531738281250F, 0.522460937500000F),
	Asura_Vector_3(0.842224121093750F, -0.208862304687500F, 0.496948242187500F),
	Asura_Vector_3(0.952026367187500F, -0.129089355468750F, 0.277343750000000F),
	Asura_Vector_3(0.952026367187500F, 0.129089355468750F, 0.277343750000000F),
	Asura_Vector_3(0.963134765625000F, -0.251831054687500F, 0.093933105468750F),
	Asura_Vector_3(0.995239257812500F, 0.000000000000000F, 0.097106933593750F),
	Asura_Vector_3(0.963134765625000F, 0.251831054687500F, 0.093933105468750F),
	Asura_Vector_3(0.927246093750000F, -0.362365722656250F, -0.093933105468750F),
	Asura_Vector_3(0.987304687500000F, -0.123229980468750F, -0.100036621093750F),
	Asura_Vector_3(0.987304687500000F, 0.123229980468750F, -0.100036621093750F),
	Asura_Vector_3(0.927246093750000F, 0.362365722656250F, -0.093933105468750F),
	Asura_Vector_3(0.846069335937500F, -0.455139160156250F, -0.277343750000000F),
	Asura_Vector_3(0.923828125000000F, -0.234069824218750F, -0.302856445312500F),
	Asura_Vector_3(0.950195312500000F, 0.000000000000000F, -0.311462402343750F),
	Asura_Vector_3(0.923828125000000F, 0.234069824218750F, -0.302856445312500F),
	Asura_Vector_3(0.846069335937500F, 0.455139160156250F, -0.277343750000000F),
	Asura_Vector_3(0.723571777343750F, -0.525695800781250F, -0.447204589843750F),
	Asura_Vector_3(0.804138183593750F, -0.326049804687500F, -0.496948242187500F),
	Asura_Vector_3(0.845397949218750F, -0.110473632812500F, -0.522460937500000F),
	Asura_Vector_3(0.845397949218750F, 0.110473632812500F, -0.522460937500000F),
	Asura_Vector_3(0.804138183593750F, 0.326049804687500F, -0.496948242187500F),
	Asura_Vector_3(0.723571777343750F, 0.525695800781250F, -0.447204589843750F),
	Asura_Vector_3(0.416931152343750F, 0.865539550781250F, 0.277343750000000F),
	Asura_Vector_3(0.171386718750000F, 0.945312500000000F, 0.277343750000000F),
	Asura_Vector_3(0.537170410156250F, 0.838195800781250F, 0.093933105468750F),
	Asura_Vector_3(0.307556152343750F, 0.946533203125000F, 0.097106933593750F),
	Asura_Vector_3(0.058044433593750F, 0.993835449218750F, 0.093933105468750F),
	Asura_Vector_3(0.631164550781250F, 0.769897460937500F, -0.093933105468750F),
	Asura_Vector_3(0.422302246093750F, 0.900878906250000F, -0.100036621093750F),
	Asura_Vector_3(0.187805175781250F, 0.977050781250000F, -0.100036621093750F),
	Asura_Vector_3(-0.058044433593750F, 0.993835449218750F, -0.093933105468750F),
	Asura_Vector_3(0.694335937500000F, 0.664001464843750F, -0.277343750000000F),
	Asura_Vector_3(0.508056640625000F, 0.806274414062500F, -0.302856445312500F),
	Asura_Vector_3(0.293640136718750F, 0.903686523437500F, -0.311462402343750F),
	Asura_Vector_3(0.062805175781250F, 0.950927734375000F, -0.302856445312500F),
	Asura_Vector_3(-0.171386718750000F, 0.945312500000000F, -0.277343750000000F),
	Asura_Vector_3(0.558593750000000F, 0.664001464843750F, -0.496948242187500F),
	Asura_Vector_3(0.366333007812500F, 0.769897460937500F, -0.522460937500000F),
	Asura_Vector_3(0.156127929687500F, 0.838195800781250F, -0.522460937500000F),
	Asura_Vector_3(-0.061584472656250F, 0.865539550781250F, -0.496948242187500F),
	Asura_Vector_3(-0.276367187500000F, 0.850646972656250F, -0.447204589843750F),
	Asura_Vector_3(-0.694335937500000F, 0.664001464843750F, 0.277343750000000F),
	Asura_Vector_3(-0.846069335937500F, 0.455139160156250F, 0.277343750000000F),
	Asura_Vector_3(-0.631164550781250F, 0.769897460937500F, 0.093933105468750F),
	Asura_Vector_3(-0.805175781250000F, 0.584960937500000F, 0.097106933593750F),
	Asura_Vector_3(-0.927246093750000F, 0.362365722656250F, 0.093933105468750F),
	Asura_Vector_3(-0.537170410156250F, 0.838195800781250F, -0.093933105468750F),
	Asura_Vector_3(-0.726257324218750F, 0.680053710937500F, -0.100036621093750F),
	Asura_Vector_3(-0.871215820312500F, 0.480529785156250F, -0.100036621093750F),
	Asura_Vector_3(-0.963134765625000F, 0.251831054687500F, -0.093933105468750F),
	Asura_Vector_3(-0.416931152343750F, 0.865539550781250F, -0.277343750000000F),
	Asura_Vector_3(-0.609802246093750F, 0.732360839843750F, -0.302856445312500F),
	Asura_Vector_3(-0.768737792968750F, 0.558532714843750F, -0.311462402343750F),
	Asura_Vector_3(-0.884948730468750F, 0.353637695312500F, -0.302856445312500F),
	Asura_Vector_3(-0.952026367187500F, 0.129089355468750F, -0.277343750000000F),
	Asura_Vector_3(-0.458862304687500F, 0.736450195312500F, -0.496948242187500F),
	Asura_Vector_3(-0.619018554687500F, 0.586303710937500F, -0.522460937500000F),
	Asura_Vector_3(-0.748901367187500F, 0.407531738281250F, -0.522460937500000F),
	Asura_Vector_3(-0.842224121093750F, 0.208862304687500F, -0.496948242187500F),
	Asura_Vector_3(-0.894409179687500F, 0.000000000000000F, -0.447204589843750F),
	Asura_Vector_3(-0.846069335937500F, -0.455139160156250F, 0.277343750000000F),
	Asura_Vector_3(-0.694335937500000F, -0.664001464843750F, 0.277343750000000F),
	Asura_Vector_3(-0.927246093750000F, -0.362365722656250F, 0.093933105468750F),
	Asura_Vector_3(-0.805175781250000F, -0.584960937500000F, 0.097106933593750F),
	Asura_Vector_3(-0.631164550781250F, -0.769897460937500F, 0.093933105468750F),
	Asura_Vector_3(-0.963134765625000F, -0.251831054687500F, -0.093933105468750F),
	Asura_Vector_3(-0.871215820312500F, -0.480529785156250F, -0.100036621093750F),
	Asura_Vector_3(-0.726257324218750F, -0.680053710937500F, -0.100036621093750F),
	Asura_Vector_3(-0.537170410156250F, -0.838195800781250F, -0.093933105468750F),
	Asura_Vector_3(-0.952026367187500F, -0.129089355468750F, -0.277343750000000F),
	Asura_Vector_3(-0.884948730468750F, -0.353637695312500F, -0.302856445312500F),
	Asura_Vector_3(-0.768737792968750F, -0.558532714843750F, -0.311462402343750F),
	Asura_Vector_3(-0.609802246093750F, -0.732360839843750F, -0.302856445312500F),
	Asura_Vector_3(-0.416931152343750F, -0.865539550781250F, -0.277343750000000F),
	Asura_Vector_3(-0.842224121093750F, -0.208862304687500F, -0.496948242187500F),
	Asura_Vector_3(-0.748901367187500F, -0.407531738281250F, -0.522460937500000F),
	Asura_Vector_3(-0.619018554687500F, -0.586303710937500F, -0.522460937500000F),
	Asura_Vector_3(-0.458862304687500F, -0.736450195312500F, -0.496948242187500F),
	Asura_Vector_3(-0.276367187500000F, -0.850646972656250F, -0.447204589843750F),
	Asura_Vector_3(0.171386718750000F, -0.945312500000000F, 0.277343750000000F),
	Asura_Vector_3(0.416931152343750F, -0.865539550781250F, 0.277343750000000F),
	Asura_Vector_3(0.058044433593750F, -0.993835449218750F, 0.093933105468750F),
	Asura_Vector_3(0.307556152343750F, -0.946533203125000F, 0.097106933593750F),
	Asura_Vector_3(0.537170410156250F, -0.838195800781250F, 0.093933105468750F),
	Asura_Vector_3(-0.058044433593750F, -0.993835449218750F, -0.093933105468750F),
	Asura_Vector_3(0.187805175781250F, -0.977050781250000F, -0.100036621093750F),
	Asura_Vector_3(0.422302246093750F, -0.900878906250000F, -0.100036621093750F),
	Asura_Vector_3(0.631164550781250F, -0.769897460937500F, -0.093933105468750F),
	Asura_Vector_3(-0.171386718750000F, -0.945312500000000F, -0.277343750000000F),
	Asura_Vector_3(0.062805175781250F, -0.950927734375000F, -0.302856445312500F),
	Asura_Vector_3(0.293640136718750F, -0.903686523437500F, -0.311462402343750F),
	Asura_Vector_3(0.508056640625000F, -0.806274414062500F, -0.302856445312500F),
	Asura_Vector_3(0.694335937500000F, -0.664001464843750F, -0.277343750000000F),
	Asura_Vector_3(-0.061584472656250F, -0.865539550781250F, -0.496948242187500F),
	Asura_Vector_3(0.156127929687500F, -0.838195800781250F, -0.522460937500000F),
	Asura_Vector_3(0.366333007812500F, -0.769897460937500F, -0.522460937500000F),
	Asura_Vector_3(0.558593750000000F, -0.664001464843750F, -0.496948242187500F),
	Asura_Vector_3(0.805175781250000F, 0.584960937500000F, -0.097106933593750F),
	Asura_Vector_3(0.726257324218750F, 0.680053710937500F, 0.100036621093750F),
	Asura_Vector_3(0.871215820312500F, 0.480529785156250F, 0.100036621093750F),
	Asura_Vector_3(0.609802246093750F, 0.732360839843750F, 0.302856445312500F),
	Asura_Vector_3(0.768737792968750F, 0.558532714843750F, 0.311462402343750F),
	Asura_Vector_3(0.884948730468750F, 0.353637695312500F, 0.302856445312500F),
	Asura_Vector_3(-0.307556152343750F, 0.946533203125000F, -0.097106933593750F),
	Asura_Vector_3(-0.422302246093750F, 0.900878906250000F, 0.100036621093750F),
	Asura_Vector_3(-0.187805175781250F, 0.977050781250000F, 0.100036621093750F),
	Asura_Vector_3(-0.508056640625000F, 0.806274414062500F, 0.302856445312500F),
	Asura_Vector_3(-0.293640136718750F, 0.903686523437500F, 0.311462402343750F),
	Asura_Vector_3(-0.062805175781250F, 0.950927734375000F, 0.302856445312500F),
	Asura_Vector_3(-0.995239257812500F, 0.000000000000000F, -0.097106933593750F),
	Asura_Vector_3(-0.987304687500000F, -0.123229980468750F, 0.100036621093750F),
	Asura_Vector_3(-0.987304687500000F, 0.123229980468750F, 0.100036621093750F),
	Asura_Vector_3(-0.923828125000000F, -0.234069824218750F, 0.302856445312500F),
	Asura_Vector_3(-0.950195312500000F, 0.000000000000000F, 0.311462402343750F),
	Asura_Vector_3(-0.923828125000000F, 0.234069824218750F, 0.302856445312500F),
	Asura_Vector_3(-0.307556152343750F, -0.946533203125000F, -0.097106933593750F),
	Asura_Vector_3(-0.187805175781250F, -0.977050781250000F, 0.100036621093750F),
	Asura_Vector_3(-0.422302246093750F, -0.900878906250000F, 0.100036621093750F),
	Asura_Vector_3(-0.062805175781250F, -0.950927734375000F, 0.302856445312500F),
	Asura_Vector_3(-0.293640136718750F, -0.903686523437500F, 0.311462402343750F),
	Asura_Vector_3(-0.508056640625000F, -0.806274414062500F, 0.302856445312500F),
	Asura_Vector_3(0.805175781250000F, -0.584960937500000F, -0.097106933593750F),
	Asura_Vector_3(0.871215820312500F, -0.480529785156250F, 0.100036621093750F),
	Asura_Vector_3(0.726257324218750F, -0.680053710937500F, 0.100036621093750F),
	Asura_Vector_3(0.884948730468750F, -0.353637695312500F, 0.302856445312500F),
	Asura_Vector_3(0.768737792968750F, -0.558532714843750F, 0.311462402343750F),
	Asura_Vector_3(0.609802246093750F, -0.732360839843750F, 0.302856445312500F),
	Asura_Vector_3(0.000000000000000F, 0.000000000000000F, -1.000000000000000F),
	Asura_Vector_3(-0.067810058593750F, 0.208862304687500F, -0.975524902343750F),
	Asura_Vector_3(0.177673339843750F, 0.129089355468750F, -0.975524902343750F),
	Asura_Vector_3(-0.132385253906250F, 0.407531738281250F, -0.903503417968750F),
	Asura_Vector_3(0.110656738281250F, 0.340698242187500F, -0.933593750000000F),
	Asura_Vector_3(0.346679687500000F, 0.251831054687500F, -0.903503417968750F),
	Asura_Vector_3(-0.190490722656250F, 0.586303710937500F, -0.787353515625000F),
	Asura_Vector_3(0.046813964843750F, 0.543151855468750F, -0.838317871093750F),
	Asura_Vector_3(0.281311035156250F, 0.466918945312500F, -0.838317871093750F),
	Asura_Vector_3(0.498718261718750F, 0.362365722656250F, -0.787353515625000F),
	Asura_Vector_3(-0.239257812500000F, 0.736450195312500F, -0.632690429687500F),
	Asura_Vector_3(-0.011230468750000F, 0.722839355468750F, -0.690856933593750F),
	Asura_Vector_3(0.217407226562500F, 0.669128417968750F, -0.710571289062500F),
	Asura_Vector_3(0.433959960937500F, 0.578186035156250F, -0.690856933593750F),
	Asura_Vector_3(0.626464843750000F, 0.455139160156250F, -0.632690429687500F),
	Asura_Vector_3(-0.219604492187500F, 0.000000000000000F, -0.975524902343750F),
	Asura_Vector_3(-0.428466796875000F, 0.000000000000000F, -0.903503417968750F),
	Asura_Vector_3(-0.289794921875000F, 0.210510253906250F, -0.933593750000000F),
	Asura_Vector_3(-0.616455078125000F, 0.000000000000000F, -0.787353515625000F),
	Asura_Vector_3(-0.502075195312500F, 0.212341308593750F, -0.838317871093750F),
	Asura_Vector_3(-0.357116699218750F, 0.411865234375000F, -0.838317871093750F),
	Asura_Vector_3(-0.774353027343750F, 0.000000000000000F, -0.632690429687500F),
	Asura_Vector_3(-0.690979003906250F, 0.212646484375000F, -0.690856933593750F),
	Asura_Vector_3(-0.569152832031250F, 0.413513183593750F, -0.710571289062500F),
	Asura_Vector_3(-0.415771484375000F, 0.591430664062500F, -0.690856933593750F),
	Asura_Vector_3(-0.067810058593750F, -0.208862304687500F, -0.975524902343750F),
	Asura_Vector_3(-0.132385253906250F, -0.407531738281250F, -0.903503417968750F),
	Asura_Vector_3(-0.289794921875000F, -0.210510253906250F, -0.933593750000000F),
	Asura_Vector_3(-0.190490722656250F, -0.586303710937500F, -0.787353515625000F),
	Asura_Vector_3(-0.357116699218750F, -0.411865234375000F, -0.838317871093750F),
	Asura_Vector_3(-0.502075195312500F, -0.212341308593750F, -0.838317871093750F),
	Asura_Vector_3(-0.239257812500000F, -0.736450195312500F, -0.632690429687500F),
	Asura_Vector_3(-0.415771484375000F, -0.591430664062500F, -0.690856933593750F),
	Asura_Vector_3(-0.569152832031250F, -0.413513183593750F, -0.710571289062500F),
	Asura_Vector_3(-0.690979003906250F, -0.212646484375000F, -0.690856933593750F),
	Asura_Vector_3(0.177673339843750F, -0.129089355468750F, -0.975524902343750F),
	Asura_Vector_3(0.346679687500000F, -0.251831054687500F, -0.903503417968750F),
	Asura_Vector_3(0.110656738281250F, -0.340698242187500F, -0.933593750000000F),
	Asura_Vector_3(0.498718261718750F, -0.362365722656250F, -0.787353515625000F),
	Asura_Vector_3(0.281311035156250F, -0.466918945312500F, -0.838317871093750F),
	Asura_Vector_3(0.046813964843750F, -0.543151855468750F, -0.838317871093750F),
	Asura_Vector_3(0.626464843750000F, -0.455139160156250F, -0.632690429687500F),
	Asura_Vector_3(0.433959960937500F, -0.578186035156250F, -0.690856933593750F),
	Asura_Vector_3(0.217407226562500F, -0.669128417968750F, -0.710571289062500F),
	Asura_Vector_3(-0.011230468750000F, -0.722839355468750F, -0.690856933593750F),
	Asura_Vector_3(0.358215332031250F, 0.000000000000000F, -0.933593750000000F),
	Asura_Vector_3(0.531005859375000F, 0.123229980468750F, -0.838317871093750F),
	Asura_Vector_3(0.531005859375000F, -0.123229980468750F, -0.838317871093750F),
	Asura_Vector_3(0.684020996093750F, 0.234069824218750F, -0.690856933593750F),
	Asura_Vector_3(0.703552246093750F, 0.000000000000000F, -0.710571289062500F),
	Asura_Vector_3(0.684020996093750F, -0.234069824218750F, -0.690856933593750F),
};
