//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=====================================================================================//

#ifndef MESHREADER_H
#define MESHREADER_H

#ifdef _WIN32
#pragma once
#endif


//-----------------------------------------------------------------------------
// This is used to read vertex and index data out of already-created meshes.
// xbox uses this a lot so it doesn't have to store sysmem backups of the
// vertex data.
//-----------------------------------------------------------------------------
class CBaseMeshReader : protected MeshDesc_t
{
// Initialization.
public:

	CBaseMeshReader();
	~CBaseMeshReader();

	// Use BeginRead/EndRead to initialize the mesh reader.
	void BeginRead( 
		IMesh* pMesh, 
		int firstVertex = 0, 
		int numVertices = 0, 
		int firstIndex = 0, 
		int numIndices = 0 );
		
	void EndRead();

	// PC can use this if it stores its own copy of meshes around, in case
	// locking static buffers is too costly.
	void BeginRead_Direct( const MeshDesc_t &desc, int numVertices, int nIndices );

	// Resets the mesh builder so it points to the start of everything again
	void Reset();
	

protected:
	IMesh *m_pMesh;
	int m_MaxVertices;
	int m_MaxIndices;
};


// A bunch of accessors for the data that CBaseMeshReader sets up.
class CMeshReader : public CBaseMeshReader
{
public:
// Access to vertex data.
public:
	int NumIndices() const;
	unsigned short Index( int index ) const;

	const Vector& Position( int iVertex ) const;

	unsigned int Color( int iVertex ) const;
	
	const float *TexCoord( int iVertex, int stage ) const;
	void TexCoord2f( int iVertex, int stage, float &s, float &t ) const;
	const Vector2D& TexCoordVector2D( int iVertex, int stage ) const;

	int NumBoneWeights() const;
	float Wrinkle( int iVertex ) const;

	const Vector &Normal( int iVertex ) const;
	void Normal( int iVertex, Vector &vNormal ) const;

	const Vector &TangentS( int iVertex ) const;
	const Vector &TangentT( int iVertex ) const;
	float BoneWeight( int iVertex ) const;

#ifdef NEW_SKINNING
	float* BoneMatrix( int iVertex ) const;
#else
	unsigned char* BoneMatrix( int iVertex ) const;
#endif
};


//-----------------------------------------------------------------------------
// CBaseMeshReader implementation.
//-----------------------------------------------------------------------------

inline CBaseMeshReader::CBaseMeshReader()
{
	m_pMesh = NULL;
}

inline CBaseMeshReader::~CBaseMeshReader()
{
	Assert( !m_pMesh );
}

inline void CBaseMeshReader::BeginRead( 
	IMesh* pMesh, 
	int firstVertex, 
	int numVertices, 
	int firstIndex, 
	int numIndices )
{
	Assert( pMesh && (!m_pMesh) );

	if ( numVertices < 0 )
	{
		numVertices = pMesh->VertexCount();
	}

	if ( numIndices < 0 )
	{
		numIndices = pMesh->IndexCount();
	}

	m_pMesh = pMesh;
	m_MaxVertices = numVertices;
	m_MaxIndices = numIndices;

	// UNDONE: support reading from compressed VBs if needed
	VertexCompressionType_t compressionType = CompressionType( pMesh->GetVertexFormat() );
	Assert( compressionType == VERTEX_COMPRESSION_NONE );
	if ( compressionType != VERTEX_COMPRESSION_NONE )
	{
		Warning( "Cannot use CBaseMeshReader with compressed vertices! Will get junk data or a crash.\n" );
	}

	// Locks mesh for modifying
	pMesh->ModifyBeginEx( true, firstVertex, numVertices, firstIndex, numIndices, *this );

	// Point to the start of the buffers..
	Reset();
}

inline void CBaseMeshReader::EndRead()
{
	Assert( m_pMesh );
	m_pMesh->ModifyEnd( *this );
	m_pMesh = NULL;
}

inline void CBaseMeshReader::BeginRead_Direct( const MeshDesc_t &desc, int nVertices, int nIndices )
{
	MeshDesc_t *pThis = this;
	*pThis = desc;
	m_MaxVertices = nVertices;
	m_MaxIndices = nIndices;

	// UNDONE: support reading from compressed verts if necessary
	Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE );
	if ( desc.m_CompressionType != VERTEX_COMPRESSION_NONE )
	{
		Warning( "Cannot use CBaseMeshReader with compressed vertices!\n" );
	}
}

inline void CBaseMeshReader::Reset()
{
}




// -------------------------------------------------------------------------------------- //
// CMeshReader implementation.
// -------------------------------------------------------------------------------------- //

inline int CMeshReader::NumIndices() const
{
	return m_MaxIndices;
}

inline unsigned short CMeshReader::Index( int index ) const
{
	Assert( (index >= 0) && (index < m_MaxIndices) );
	return m_pIndices[index * m_nIndexSize];
}

inline const Vector& CMeshReader::Position( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	return *(Vector*)((char*)m_pPosition + iVertex * m_VertexSize_Position);
}

inline unsigned int CMeshReader::Color( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	unsigned char *pColor = m_pColor + iVertex * m_VertexSize_Color;
	return (pColor[0] << 16) | (pColor[1] << 8) | (pColor[2]) | (pColor[3] << 24);
}

inline const float *CMeshReader::TexCoord( int iVertex, int iStage ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	return (float*)( (char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage] );
}

inline void CMeshReader::TexCoord2f( int iVertex, int iStage, float &s, float &t ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	float *p = (float*)( (char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage] );
	s = p[0];
	t = p[1];
}

inline const Vector2D& CMeshReader::TexCoordVector2D( int iVertex, int iStage ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	Vector2D *p = (Vector2D*)( (char*)m_pTexCoord[iStage] + iVertex * m_VertexSize_TexCoord[iStage] );
	return *p;
}

inline float CMeshReader::Wrinkle( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	return *(float*)( (char*)m_pWrinkle + iVertex * m_VertexSize_Wrinkle );
}

inline int CMeshReader::NumBoneWeights() const
{
	return m_NumBoneWeights;
}

inline const Vector &CMeshReader::Normal( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	return *(const Vector *)(const float*)( (char*)m_pNormal + iVertex  * m_VertexSize_Normal );
}

inline void CMeshReader::Normal( int iVertex, Vector &vNormal ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	const float *p = (const float*)( (char*)m_pNormal + iVertex * m_VertexSize_Normal );
	vNormal.Init( p[0], p[1], p[2] );
}

inline const Vector &CMeshReader::TangentS( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	return *(const Vector*)( (char*)m_pTangentS + iVertex * m_VertexSize_TangentS );
}

inline const Vector &CMeshReader::TangentT( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	return *(const Vector*)( (char*)m_pTangentT + iVertex * m_VertexSize_TangentT );
}

inline float CMeshReader::BoneWeight( int iVertex ) const
{
	Assert( iVertex >= 0 && iVertex < m_MaxVertices );
	float *p = (float*)( (char*)m_pBoneWeight + iVertex * m_VertexSize_BoneWeight );
	return *p;
}

#endif // MESHREADER_H