//--------------------------------------------------------------------------------
GeometryPtr GeometryLoaderDX11::loadStanfordPlyFile( std::wstring filename, bool withAdjacency )
{
	// Get the file path to the models
	FileSystem fs;
	filename = fs.GetModelsFolder() + filename;

	// Load the contents of the file
	std::ifstream fin;

	// Open the file and read the MS3D header data
	fin.open( filename.c_str(), std::ios::in );

	if(!fin.is_open())
	{
		// signal error - bad filename?
		throw new std::exception( "Could not open file" );
	}

	// Parse the input
	std::string txt;

	// Read in header
	std::getline(fin, txt);

	if( 0 != txt.compare( "ply" ) )
	{
		// signal error - not a PLY format file
		throw new std::exception( "File does not contain the correct header - 'PLY' expected." );
	}

	std::getline(fin, txt);

	if( 0 != txt.compare( "format ascii 1.0" ) )
	{
		// signal error - not a format of PLY that this code supports
		throw new std::exception( "File is not correct format - ASCII 1.0 expected." );
	}

	std::vector< PlyElementDesc > elements;

	// Read in the rest of the header
	while(fin.is_open() && !fin.eof())
	{
		// Grab the next line of the header
		std::getline(fin, txt);

		// If we're at the end then stop processing
		if(0 == txt.compare("end_header"))
		{
			break;
		}
		// If this line is a comment, skip to the next line
		else if(0 == txt.compare(0, 7, "comment"))
		{
			continue;
		}
		// If this line is an element, process it
		else if(0 == txt.compare(0, 7, "element"))
		{
			elements.push_back(ParsePLYElementHeader( txt, fin ));
		}
		// Otherwise, wtf?
		else
		{
			throw new std::exception("File header contains unexpected line beginning");
		}
	}

	// Read all the raw data
	for( std::vector< PlyElementDesc >::iterator it = elements.begin(); it != elements.end(); ++it)
	{
		(*it).data = ReadPLYElementData(fin, *it);
	}

	// Create a resource to contain the geometry
	GeometryPtr MeshPtr = GeometryPtr( new GeometryDX11() );

	// Convert data to D3D11 format
	int elemIdx = -1;

	// Pull out all the vertex data
	if(-1 < (elemIdx = FindPlyElementIndex(elements, "vertex")))
	{
		PlyElementDesc d = elements.at( elemIdx );
		
		// Has positions?
		int xIdx = FindPlyElementPropertyIndex( d.dataFormat, "x" );
		int yIdx = FindPlyElementPropertyIndex( d.dataFormat, "y" );
		int zIdx = FindPlyElementPropertyIndex( d.dataFormat, "z" );

		if ((-1 != xIdx) && (-1 != yIdx) && (-1 != zIdx))
		{
			VertexElementDX11 *pPositions = new VertexElementDX11( 3, d.elementCount );
			pPositions->m_SemanticName = VertexElementDX11::PositionSemantic;
			pPositions->m_uiSemanticIndex = 0;
			pPositions->m_Format = DXGI_FORMAT_R32G32B32_FLOAT;
			pPositions->m_uiInputSlot = 0;
			pPositions->m_uiAlignedByteOffset = 0;
			pPositions->m_InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
			pPositions->m_uiInstanceDataStepRate = 0;

			Vector3f* pRawPos = pPositions->Get3f( 0 );

			for(int v = 0; v < d.elementCount; ++v)
			{
				void** raw = d.data.at(v);

				float x = *reinterpret_cast<float*>(raw[xIdx]);
				float y = *reinterpret_cast<float*>(raw[yIdx]);
				float z = *reinterpret_cast<float*>(raw[zIdx]);

				pRawPos[v] = Vector3f( x, y, z );
			}

			MeshPtr->AddElement( pPositions );
		}

		// Has normals?
		int nxIdx = FindPlyElementPropertyIndex( d.dataFormat, "nx" );
		int nyIdx = FindPlyElementPropertyIndex( d.dataFormat, "ny" );
		int nzIdx = FindPlyElementPropertyIndex( d.dataFormat, "nz" );

		if ((-1 != nxIdx) && (-1 != nyIdx) && (-1 != nzIdx))
		{
			VertexElementDX11 *pNormals = new VertexElementDX11( 3, d.elementCount );
			pNormals->m_SemanticName = VertexElementDX11::NormalSemantic;
			pNormals->m_uiSemanticIndex = 0;
			pNormals->m_Format = DXGI_FORMAT_R32G32B32_FLOAT;
			pNormals->m_uiInputSlot = 0;
			pNormals->m_uiAlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
			pNormals->m_InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
			pNormals->m_uiInstanceDataStepRate = 0;

			Vector3f* pRawNorms = pNormals->Get3f( 0 );

			for(int v = 0; v < d.elementCount; ++v)
			{
				void** raw = d.data.at(v);

				float x = *reinterpret_cast<float*>(raw[nxIdx]);
				float y = *reinterpret_cast<float*>(raw[nyIdx]);
				float z = *reinterpret_cast<float*>(raw[nzIdx]);

				pRawNorms[v] = Vector3f( x, y, z );
			}

			MeshPtr->AddElement( pNormals );
		}
	}
	else
	{
		throw new std::exception("Expected a 'vertex' element, but not found");
	}

	// Pull out all the face index data
	if(-1 < (elemIdx = FindPlyElementIndex(elements, "face")))
	{
		PlyElementDesc d = elements.at( elemIdx );

		// Firstly, assert that the format is correct
		if((1 != d.dataFormat.size()) && d.dataFormat.at(0).isList && (0 == d.dataFormat.at(0).type.compare("uint")))
		{
			// Expect a single list of integers
			throw new std::exception("Expected 'face' to be a single list of integers per-face");
		}

		// Secondly, assert that each list is of the same dimension
		int faceSize = -1;
		for(int f = 0; f < d.elementCount; ++f)
		{
			void** raw = d.data.at(f);
			PlyDataArray<int>* idxs = reinterpret_cast<PlyDataArray<int>*>(raw[0]);
			
			if( -1 == faceSize)
				faceSize = idxs->length;
			else if(faceSize != idxs->length)
				throw new std::exception("Expected each face to have the same number of indexes");
		}

		if(withAdjacency)
		{
			MeshPtr->SetPrimitiveType( (D3D11_PRIMITIVE_TOPOLOGY)(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + ((2*faceSize) - 1)) );

			// Grab all of the faces so we can search for adjacency
			int* pRaw = new int[d.elementCount * faceSize];

			int pRawIdx = 0;

			for(int f = 0; f < d.elementCount; ++f)
			{
				void** raw = d.data.at(f);
				PlyDataArray<int>* idxs = reinterpret_cast<PlyDataArray<int>*>(raw[0]);

				for(unsigned int fi = 0; fi < idxs->length; ++fi)
					pRaw[pRawIdx++] = idxs->data[fi];
			}

			// We can now go and add the actual indices
			for(int f = 0; f < (d.elementCount * faceSize); f+=3)
			{
				MeshPtr->AddIndex( pRaw[f + 0] );
				MeshPtr->AddIndex( pRaw[f + 1] );
				MeshPtr->AddIndex( pRaw[f + 2] );

				// We now need to find an adjacency for each
				// edge where possible
				int a0 = FindAdjacentIndex( pRaw[f + 0], pRaw[f + 1], pRaw[f + 2], pRaw, d.elementCount * faceSize );
				int a1 = FindAdjacentIndex( pRaw[f + 1], pRaw[f + 2], pRaw[f + 0], pRaw, d.elementCount * faceSize );
				int a2 = FindAdjacentIndex( pRaw[f + 2], pRaw[f + 0], pRaw[f + 1], pRaw, d.elementCount * faceSize );

				std::wstringstream out;
				out << "Actual indices <" << pRaw[f+0] << ", " << pRaw[f+1] << ", " << pRaw[f+2] << "> have adjacency <" << a0 << ", " << a1 << ", " << a2 << ">.";
				OutputDebugString( out.str().c_str() );
				OutputDebugString( L"\n" );

				MeshPtr->AddIndex( a0 );
				MeshPtr->AddIndex( a1 );
				MeshPtr->AddIndex( a2 );
			}

			delete[] pRaw;
		}
		else
		{
			// Thirdly, can now set the appropriate topology
			MeshPtr->SetPrimitiveType( (D3D11_PRIMITIVE_TOPOLOGY)(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST + (faceSize - 1)) );

			// Finally, extract this data
			for(int f = 0; f < d.elementCount; ++f)
			{
				void** raw = d.data.at(f);
				PlyDataArray<int>* idxs = reinterpret_cast<PlyDataArray<int>*>(raw[0]);

				for(unsigned int fi = 0; fi < idxs->length; ++fi)
					MeshPtr->AddIndex( idxs->data[fi] );
			}
		}
	}
	else
	{
		throw new std::exception("Expected a 'face' element, but not found");
	}

	// Push into renderable resource
	MeshPtr->LoadToBuffers( );

	// Release all intermediary memory
	for( std::vector< PlyElementDesc >::iterator it = elements.begin(); it != elements.end(); ++it)
	{
		PlyElementDesc d = *it;
		for(int e = 0; e < d.elementCount; ++e)
		{
			void** raw = d.data.at(e);

			if(d.dataFormat.at(0).isList)
			{
				PlyDataArray<void*>* rawArray = reinterpret_cast<PlyDataArray<void*>*>(raw[0]);
				SAFE_DELETE_ARRAY( rawArray->data );
				SAFE_DELETE(raw[0]);
			}
			else
			{
				SAFE_DELETE(raw[0]);
			}
		}
	}

	// Return to caller
	return MeshPtr;
}