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

#define DISABLE_PROTECTED_THINGS
#include "locald3dtypes.h"
#include "imeshdx8.h"
#include "shaderapidx8_global.h"
#include "materialsystem/IShader.h"
#include "tier0/vprof.h"
#include "studio.h"
#include "tier1/fmtstr.h"

#include "tier0/platform.h"
#include "tier0/systeminformation.h"

// fixme - stick this in a header file.
#if defined( _DEBUG ) && !defined( _X360 )
// define this if you want to range check all indices when drawing
#define CHECK_INDICES
#endif
#ifdef CHECK_INDICES
#define CHECK_INDICES_MAX_NUM_STREAMS 2
#endif

#include "dynamicib.h"
#include "dynamicvb.h"
#include "utlvector.h"
#include "shaderapi/ishaderapi.h"
#include "imaterialinternal.h"
#include "imaterialsysteminternal.h"
#include "shaderapidx8.h"
#include "shaderapi/ishaderutil.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "materialsystem/materialsystem_config.h"
#include "materialsystem/ivballoctracker.h"
#include "tier1/strtools.h"
#include "convar.h"
#include "shaderdevicedx8.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// Uncomment this to test buffered state
//-----------------------------------------------------------------------------
//#define DEBUG_BUFFERED_MESHES 1

#define MAX_DX8_STREAMS 16

#define VERTEX_FORMAT_INVALID	0xFFFFFFFFFFFFFFFFull

// this is hooked into the engines convar
extern ConVar mat_debugalttab;

//#define DRAW_SELECTION 1
static bool g_bDrawSelection = true;	// only used in DRAW_SELECTION 
static unsigned short g_nScratchIndexBuffer[6]; // large enough for a fast quad; used when device is not active
#ifdef _DEBUG
int CVertexBuffer::s_BufferCount = 0;
int CIndexBuffer::s_BufferCount = 0;
#endif

//-----------------------------------------------------------------------------
// Important enumerations
//-----------------------------------------------------------------------------
enum
{
	VERTEX_BUFFER_SIZE = 32768,
	MAX_QUAD_INDICES = 16384,
};


//-----------------------------------------------------------------------------
//
// Code related to vertex buffers start here
//
//-----------------------------------------------------------------------------
class CVertexBufferDx8 : public CVertexBufferBase
{
	typedef CVertexBufferBase BaseClass;

	// Methods of IVertexBuffer
public:
	virtual int VertexCount() const;
	virtual VertexFormat_t GetVertexFormat() const;
	virtual bool IsDynamic() const;
	virtual void BeginCastBuffer( VertexFormat_t format );
	virtual void EndCastBuffer( );
	virtual int GetRoomRemaining() const;
	virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc );
	virtual void Unlock( int nVertexCount, VertexDesc_t &desc );

public:
	// constructor
	CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName );
	virtual ~CVertexBufferDx8();

	// Allocates, deallocates the index buffer
	bool Allocate( );
	void Free();

	// Returns the vertex size
	int VertexSize() const;

	// Only used by dynamic buffers, indicates the next lock should perform a discard.
	void Flush();

	// Returns the D3D buffer
	IDirect3DVertexBuffer9* GetDx9Buffer();

	// Used to measure how much static buffer memory is touched each frame 
	void HandlePerFrameTextureStats( int nFrame );

protected:
	IDirect3DVertexBuffer9 *m_pVertexBuffer;
	VertexFormat_t m_VertexFormat;
	int m_nVertexCount;
	int m_nBufferSize;
	int m_nFirstUnwrittenOffset;	// Used only for dynamic buffers, indicates where it's safe to write (nooverwrite)

	// Is it locked?
	bool m_bIsLocked : 1;
	bool m_bIsDynamic : 1;
	bool m_bFlush : 1;				// Used only for dynamic buffers, indicates to discard the next time

#ifdef VPROF_ENABLED
	int m_nVProfFrame;
	int	*m_pFrameCounter;
	int	*m_pGlobalCounter;
#endif

#ifdef _DEBUG
	static int s_nBufferCount;
#endif
};


//-----------------------------------------------------------------------------
//
// Code related to index buffers start here
//
//-----------------------------------------------------------------------------
class CIndexBufferDx8 : public CIndexBufferBase
{
	typedef CIndexBufferBase BaseClass;

	// Methods of IIndexBuffer
public:
	virtual int IndexCount( ) const;
	virtual MaterialIndexFormat_t IndexFormat() const;
	virtual int GetRoomRemaining() const;
	virtual bool Lock( int nIndexCount, bool bAppend, IndexDesc_t &desc );
	virtual void Unlock( int nIndexCount, IndexDesc_t &desc );
	virtual void BeginCastBuffer( MaterialIndexFormat_t format );
	virtual void EndCastBuffer( );
	virtual bool IsDynamic() const;
	virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); }
	virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); }

public:
	// constructor
	CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName );
	virtual ~CIndexBufferDx8();

	// Allocates, deallocates the index buffer
	bool Allocate( );
	void Free();

	// Returns the index size
	int IndexSize() const;

	// Only used by dynamic buffers, indicates the next lock should perform a discard.
	void Flush();

	// Returns the D3D buffer
	IDirect3DIndexBuffer9* GetDx9Buffer();

	// Used to measure how much static buffer memory is touched each frame 
	void HandlePerFrameTextureStats( int nFrame );

#ifdef CHECK_INDICES
	unsigned short GetShadowIndex( int i ) const;
#endif

private:
	IDirect3DIndexBuffer9 *m_pIndexBuffer;
	MaterialIndexFormat_t m_IndexFormat;
	int m_nIndexCount;
	int m_nBufferSize;
	int m_nFirstUnwrittenOffset;	// Used only for dynamic buffers, indicates where it's safe to write (nooverwrite)

	// Is it locked?
	bool m_bIsLocked : 1;
	bool m_bIsDynamic : 1;
	bool m_bFlush : 1;				// Used only for dynamic buffers, indicates to discard the next time

#ifdef CHECK_INDICES
	unsigned char *m_pShadowIndices;
	void *m_pLockIndexBuffer;
	int m_nLockIndexBufferSize;
	int m_nLockIndexOffset;
#endif

#ifdef VPROF_ENABLED
	int m_nVProfFrame;
#endif

#ifdef _DEBUG
	static int s_nBufferCount;
#endif
};


//-----------------------------------------------------------------------------
//
// Backward compat mesh code; will go away soon
//
//-----------------------------------------------------------------------------
abstract_class CBaseMeshDX8 : public CMeshBase
{
public:
	// constructor, destructor
	CBaseMeshDX8();
	virtual ~CBaseMeshDX8();

	// FIXME: Make this work! Unsupported methods of IIndexBuffer + IVertexBuffer
	virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t& desc ) { Assert(0); return false; }
	virtual void Unlock( int nWrittenIndexCount, IndexDesc_t& desc ) { Assert(0); }
	virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); }
	virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); }
	virtual void Spew( int nIndexCount, const IndexDesc_t & desc ) { Assert(0); }
	virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) { Assert(0); }
	virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) { Assert(0); return false; }
	virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) { Assert(0); }
	virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) { Assert(0); }
	virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) { Assert(0); }

	// Locks mesh for modifying
	void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
	void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
	void ModifyEnd( MeshDesc_t& desc );

	// Sets/gets the vertex format
	virtual void SetVertexFormat( VertexFormat_t format );
	virtual VertexFormat_t GetVertexFormat() const;

	// Sets/gets the morph format
	virtual void SetMorphFormat( MorphFormat_t format );
	virtual MorphFormat_t GetMorphFormat() const;
	// Am I using morph data?
	virtual bool IsUsingMorphData() const;
	bool IsUsingVertexID() const 
	{ 
		return ShaderAPI()->GetBoundMaterial()->IsUsingVertexID();
	}

	// Sets the material
	virtual void SetMaterial( IMaterial* pMaterial );

	// returns the # of vertices (static meshes only)
	int VertexCount() const { return 0; }

	void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes )
	{
		Assert( 0 );
	}

	void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes )
	{
		Assert( pMesh == NULL && nVertexOffsetInBytes == 0 );
	}

	void DisableFlexMesh( )
	{
		Assert( 0 );
	}

	void MarkAsDrawn() {}

	bool HasColorMesh( ) const { return false; }
	bool HasFlexMesh( ) const { return false; }
	
	// Draws the mesh
	void DrawMesh( );

	// Begins a pass
	void BeginPass( );

	// Spews the mesh data
	virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t & desc );

	// Call this in debug mode to make sure our data is good.
	virtual void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & desc );

	virtual void HandleLateCreation( ) = 0;

	void Draw( CPrimList *pLists, int nLists );

	// Copy verts and/or indices to a mesh builder. This only works for temp meshes!
	virtual void CopyToMeshBuilder( 
		int iStartVert,		// Which vertices to copy.
		int nVerts, 
		int iStartIndex,	// Which indices to copy.
		int nIndices, 
		int indexOffset,	// This is added to each index.
		CMeshBuilder &builder );

	// returns the primitive type
	virtual MaterialPrimitiveType_t GetPrimitiveType() const = 0;

	// Returns the number of indices in a mesh..
	virtual int IndexCount( ) const = 0;

	// FIXME: Make this work!
	virtual MaterialIndexFormat_t IndexFormat() const { return MATERIAL_INDEX_FORMAT_16BIT; }

	// NOTE: For dynamic index buffers only!
	// Casts the memory of the dynamic index buffer to the appropriate type
	virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); }
	virtual void BeginCastBuffer( VertexFormat_t format ) { Assert(0); }
	virtual void EndCastBuffer( ) { Assert(0); }
	virtual int GetRoomRemaining() const { Assert(0); return 0; }

	// returns a static vertex buffer...
	virtual CVertexBuffer *GetVertexBuffer() { return 0; }
	virtual CIndexBuffer *GetIndexBuffer() { return 0; }

	// Do I need to reset the vertex format?
	virtual bool NeedsVertexFormatReset( VertexFormat_t fmt ) const;

	// Do I have enough room?
	virtual bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const;

	// Operation to do pre-lock
	virtual void PreLock() {}

	virtual unsigned ComputeMemoryUsed();

	bool m_bMeshLocked;

protected:
	bool DebugTrace() const;
	
	// The vertex format we're using...
	VertexFormat_t m_VertexFormat;
	
	// The morph format we're using
	MorphFormat_t m_MorphFormat;

#ifdef DBGFLAG_ASSERT
	IMaterialInternal* m_pMaterial;
	bool m_IsDrawing;
#endif
};

//-----------------------------------------------------------------------------
// Implementation of the mesh
//-----------------------------------------------------------------------------
class CMeshDX8 : public CBaseMeshDX8
{
public:
	// constructor
	CMeshDX8( const char *pTextureGroupName );
	virtual ~CMeshDX8();

	// Locks/unlocks the mesh
	void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );
	void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );

	// Locks mesh for modifying
	void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
	void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
	void ModifyEnd( MeshDesc_t& desc );

	// returns the # of vertices (static meshes only)
	int VertexCount() const;

	// returns the # of indices 
	virtual int IndexCount( ) const;

	// Sets up the vertex and index buffers
	void UseIndexBuffer( CIndexBuffer* pBuffer );
	void UseVertexBuffer( CVertexBuffer* pBuffer );

	// returns a static vertex buffer...
	CVertexBuffer *GetVertexBuffer() { return m_pVertexBuffer; }
	CIndexBuffer *GetIndexBuffer() { return m_pIndexBuffer; }

	void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes );
	void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes );
	void DisableFlexMesh();

	virtual void HandleLateCreation( );

	bool HasColorMesh( ) const;
	bool HasFlexMesh( ) const;

	// Draws the mesh
	void Draw( int nFirstIndex, int nIndexCount );
	void Draw( CPrimList *pLists, int nLists );
	void DrawInternal( CPrimList *pLists, int nLists );

	// Draws a single pass
	void RenderPass();

	// Sets the primitive type
	void SetPrimitiveType( MaterialPrimitiveType_t type );
	MaterialPrimitiveType_t GetPrimitiveType() const;

	bool IsDynamic() const { return false; }

protected:
	// Sets the render state.
	bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID );

	// Is the vertex format valid?
	bool IsValidVertexFormat( VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID );

	// Locks/ unlocks the vertex buffer
	bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc );
	void Unlock( int nVertexCount, VertexDesc_t &desc );

	// Locks/unlocks the index buffer
	// Pass in nFirstIndex=-1 to lock wherever the index buffer is. Pass in a value 
	// >= 0 to specify where to lock.
	int  Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &pIndices );
	void Unlock( int nIndexCount, IndexDesc_t &desc );

	// computes how many primitives we've got
	int NumPrimitives( int nVertexCount, int nIndexCount ) const;

	// Debugging output...
	void SpewMaterialVerts( );

	// Stream source setting methods
	void SetVertexIDStreamState( );
	void SetColorStreamState( );
	void SetVertexStreamState( int nVertOffsetInBytes );
	void SetIndexStreamState( int firstVertexIdx );

	void CheckIndices( CPrimList *pPrim, int numPrimitives );

	// The vertex and index buffers
	CVertexBuffer* m_pVertexBuffer;
	CIndexBuffer* m_pIndexBuffer;

	// The current color mesh (to be bound to stream 1)
	// The vertex offset allows use of a global, shared color mesh VB
	CMeshDX8 *	m_pColorMesh;
	int			m_nColorMeshVertOffsetInBytes;

	CVertexBuffer *m_pFlexVertexBuffer;

	bool   m_bHasFlexVerts;
	int	   m_nFlexVertOffsetInBytes;
	int m_flexVertCount;

	// Primitive type
	MaterialPrimitiveType_t m_Type;

	// Primitive mode
	D3DPRIMITIVETYPE m_Mode;

	// Number of primitives
	unsigned int m_NumIndices;
	unsigned short m_NumVertices;

	// Is it locked?
	bool m_IsVBLocked;
	bool m_IsIBLocked;

	// Used in rendering sub-parts of the mesh
	static CPrimList *s_pPrims;
	static int s_nPrims;
	static unsigned int s_FirstVertex; // Gets reset during CMeshDX8::DrawInternal
	static unsigned int s_NumVertices;
	int	m_FirstIndex;

#ifdef RECORDING
	int	m_LockVertexBufferSize;
	void *m_LockVertexBuffer;
#endif

#if defined( RECORDING ) || defined( CHECK_INDICES )
	void *m_LockIndexBuffer;
	int	m_LockIndexBufferSize;
#endif
	const char *m_pTextureGroupName;

	friend class CMeshMgr; // MESHFIXME
};


//-----------------------------------------------------------------------------
// A little extra stuff for the dynamic version
//-----------------------------------------------------------------------------
class CDynamicMeshDX8 : public CMeshDX8
{
public:
	// constructor, destructor
	CDynamicMeshDX8();
	virtual ~CDynamicMeshDX8();

	// Initializes the dynamic mesh
	void Init( int nBufferId );

	// Sets the vertex format
	virtual void SetVertexFormat( VertexFormat_t format );

	// Resets the state in case of a task switch
	void Reset();

	// Do I have enough room in the buffer?
	bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const;

	// returns the # of indices
	int IndexCount( ) const;

	// Locks the mesh
	void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );

	// Unlocks the mesh
	void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );

	// Override vertex + index buffer
	void OverrideVertexBuffer( CVertexBuffer *pStaticVertexBuffer );
	void OverrideIndexBuffer( CIndexBuffer *pStaticIndexBuffer );

	// Do I need to reset the vertex format?
	bool NeedsVertexFormatReset(VertexFormat_t fmt) const;

	// Draws it						   
	void Draw( int nFirstIndex, int nIndexCount );
	void MarkAsDrawn() { m_HasDrawn = true; }
	// Simply draws what's been buffered up immediately, without state change 
	void DrawSinglePassImmediately();

	// Operation to do pre-lock
	void PreLock();

	bool IsDynamic() const { return true; }

private:
	// Resets buffering state
	void ResetVertexAndIndexCounts();

	// Buffer Id
	int m_nBufferId;

	// total queued vertices
	int m_TotalVertices;
	int	m_TotalIndices;

	// the first vertex and index since the last draw
	int m_nFirstVertex;
	int m_FirstIndex;

	// Have we drawn since the last lock?
	bool m_HasDrawn;

	// Any overrides?
	bool m_VertexOverride;
	bool m_IndexOverride;
};


//-----------------------------------------------------------------------------
// A mesh that stores temporary vertex data in the correct format (for modification)
//-----------------------------------------------------------------------------
class CTempMeshDX8 : public CBaseMeshDX8
{
public:
	// constructor, destructor
	CTempMeshDX8( bool isDynamic );
	virtual ~CTempMeshDX8();

	// Sets the material
	virtual void SetVertexFormat( VertexFormat_t format );

	// Locks/unlocks the mesh
	void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc );
	void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc );

	// Locks mesh for modifying
	virtual void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
	virtual void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
	virtual void ModifyEnd( MeshDesc_t& desc );

	// Number of indices + vertices
	int VertexCount() const;
	virtual int IndexCount() const;
	virtual bool IsDynamic() const;

	// Sets the primitive type
	void SetPrimitiveType( MaterialPrimitiveType_t type );
	MaterialPrimitiveType_t GetPrimitiveType() const;

	// Begins a pass
	void BeginPass( );

	// Draws a single pass
	void RenderPass();

	virtual void HandleLateCreation() 
	{ 
		Assert( !"TBD - CTempMeshDX8::HandleLateCreation()" ); 
	}

	// Draws the entire beast
	void Draw( int nFirstIndex, int nIndexCount );

	virtual void CopyToMeshBuilder( 
		int iStartVert,		// Which vertices to copy.
		int nVerts, 
		int iStartIndex,	// Which indices to copy.
		int nIndices, 
		int indexOffset,	// This is added to each index.
		CMeshBuilder &builder );
private:
	// Selection mode 
	void TestSelection( );
	void ClipTriangle( D3DXVECTOR3 **ppVert, float zNear, D3DXMATRIX &proj );

	CDynamicMeshDX8 *GetDynamicMesh();

	CUtlVector< unsigned char, CUtlMemoryAligned< unsigned char, 32 > > m_VertexData;
	CUtlVector< unsigned short > m_IndexData;

	unsigned short m_VertexSize;
	MaterialPrimitiveType_t m_Type;
	int m_LockedVerts;
	int m_LockedIndices;
	bool m_IsDynamic;

	// Used in rendering sub-parts of the mesh
	static unsigned int s_NumIndices;
	static unsigned int s_FirstIndex;

#ifdef DBGFLAG_ASSERT
	bool m_Locked;
	bool m_InPass;
#endif
};

#if 0
//-----------------------------------------------------------------------------
// A mesh that stores temporary vertex data in the correct format (for modification)
//-----------------------------------------------------------------------------
class CTempIndexBufferDX8 : public CIndexBufferBase
{
public:
	// constructor, destructor
	CTempIndexBufferDX8( bool isDynamic );
	virtual ~CTempIndexBufferDX8();

	// Locks/unlocks the mesh
	void LockIndexBuffer( int nIndexCount );
	void UnlockMesh( int nIndexCount );

	// Locks mesh for modifying
	virtual void ModifyBeginEx( bool bReadOnly, int nFirstIndex, int nIndexCount );
	virtual void ModifyEnd();

	// Number of indices
	virtual int IndexCount() const;
	virtual bool IsDynamic() const;

	virtual void CopyToIndexBuilder( 
		int iStartIndex,	// Which indices to copy.
		int nIndices, 
		int indexOffset,	// This is added to each index.
		CIndexBuilder &builder );
private:
	// Selection mode 
	void TestSelection( );

	CDynamicMeshDX8 *GetDynamicMesh();

	CUtlVector< unsigned short > m_IndexData;

	MaterialPrimitiveType_t m_Type;
	int m_LockedIndices;
	bool m_IsDynamic;

	// Used in rendering sub-parts of the mesh
	static unsigned int s_NumIndices;
	static unsigned int s_FirstIndex;

#ifdef DBGFLAG_ASSERT
	bool m_Locked;
	bool m_InPass;
#endif
};
#endif
//-----------------------------------------------------------------------------
// This is a version that buffers up vertex data so we can blast through it later
//-----------------------------------------------------------------------------
class CBufferedMeshDX8 : public CBaseMeshDX8
{
public:
	// constructor, destructor
	CBufferedMeshDX8();
	virtual ~CBufferedMeshDX8();

	// checks to see if it was rendered..
	void ResetRendered();
	bool WasNotRendered() const;

	// Sets the mesh we're really going to draw into
	void SetMesh( CBaseMeshDX8* pMesh );
	const CBaseMeshDX8* GetMesh() const { return m_pMesh; }

	// Spews the mesh data
	virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc );

	// Sets the vertex format
	virtual void SetVertexFormat( VertexFormat_t format );
	virtual VertexFormat_t GetVertexFormat() const;

	// Sets the morph format
	virtual void SetMorphFormat( MorphFormat_t format );

	// Sets the material
	void SetMaterial( IMaterial *pMaterial );

	// returns the number of indices (should never be called!)
	virtual int IndexCount() const { Assert(0); return 0; }
	virtual MaterialIndexFormat_t IndexFormat() const { Assert(0); return MATERIAL_INDEX_FORMAT_16BIT; }
	virtual bool IsDynamic() const { Assert(0); return true; }
	virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); }
	virtual void EndCastBuffer( ) { Assert(0); }
	virtual int GetRoomRemaining() const { Assert(0); return 0; }

	// Locks the mesh
	void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );
	void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );

	// Sets the primitive type
	void SetPrimitiveType( MaterialPrimitiveType_t type );
	MaterialPrimitiveType_t GetPrimitiveType( ) const;

	void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & spewDesc );

	virtual void HandleLateCreation( ) 
	{ 
		if ( m_pMesh ) 
		{
			m_pMesh->HandleLateCreation(); 
		}
	}

	// Draws it
	void Draw( int nFirstIndex, int nIndexCount );

	// Renders a pass
	void RenderPass();

	// Flushes queued data
	void Flush( );

	void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes );

private:
	// The actual mesh we need to render....
	CBaseMeshDX8* m_pMesh;
	
	// The index of the last vertex (for tristrip fixup)
	unsigned short m_LastIndex;

	// Extra padding indices for tristrips
	unsigned short m_ExtraIndices;

	// Am I currently flushing?
	bool m_IsFlushing;

	// has the dynamic mesh been rendered?
	bool m_WasRendered;

	// Do I need to flush?
	bool m_FlushNeeded;

#ifdef DEBUG_BUFFERED_MESHES
	// for debugging only
	bool m_BufferedStateSet;
	BufferedState_t m_BufferedState;
#endif
};


//-----------------------------------------------------------------------------
// Implementation of the mesh manager
//-----------------------------------------------------------------------------
class CMeshMgr : public IMeshMgr
{
public:
	// constructor, destructor
	CMeshMgr();
	virtual ~CMeshMgr();

	// Initialize, shutdown
	void Init();
	void Shutdown();

	// Task switch...
	void ReleaseBuffers();
	void RestoreBuffers();

	// Releases all dynamic vertex buffers
	void DestroyVertexBuffers();

	// Flushes the dynamic mesh
	void Flush();

	// Flushes the vertex buffers
	void DiscardVertexBuffers();

	// Creates, destroys static meshes
	IMesh *CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial *pMaterial = NULL );
	void DestroyStaticMesh( IMesh *pMesh );

	// Gets at the dynamic mesh	(spoofs it though)
	IMesh *GetDynamicMesh( IMaterial *pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool buffered,
		IMesh *pVertexOverride, IMesh *pIndexOverride );

// -----------------------------------------------------------
// ------------ New Vertex/Index Buffer interface ----------------------------
	// Do we need support for bForceTempMesh and bSoftwareVertexShader?
	// I don't think we use bSoftwareVertexShader anymore. .need to look into bForceTempMesh.
	IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup );
	IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup );
	void DestroyVertexBuffer( IVertexBuffer * );
	void DestroyIndexBuffer( IIndexBuffer * );
	// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams?
	IVertexBuffer *GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered = true );
	IIndexBuffer *GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered = true );
	void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 );
	void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes );
	void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount );
// ------------ End ----------------------------
	void RenderPassWithVertexAndIndexBuffers( void );

	VertexFormat_t GetCurrentVertexFormat( void ) const { return m_CurrentVertexFormat; }

	// Gets at the *actual* dynamic mesh
	IMesh*	GetActualDynamicMesh( VertexFormat_t vertexFormat );
	IMesh	*GetFlexMesh();

	// Computes vertex format from a list of ingredients
	VertexFormat_t ComputeVertexFormat( unsigned int flags, 
				int numTexCoords, int *pTexCoordDimensions, int numBoneWeights,
				int userDataSize ) const;

	// Use fat vertices (for tools)
	virtual void UseFatVertices( bool bUseFat );

	// Returns the number of vertices we can render using the dynamic mesh
	virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices );
	virtual int GetMaxVerticesToRender( IMaterial *pMaterial );
	virtual int GetMaxIndicesToRender( );

	// Returns a vertex buffer appropriate for the flags
	CVertexBuffer *FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t fmt );
	CIndexBuffer *GetDynamicIndexBuffer();

	// Is the mesh dynamic?
	bool IsDynamicMesh( IMesh *pMesh ) const;
	bool IsBufferedDynamicMesh( IMesh *pMesh ) const;

	// Is the vertex buffer dynamic?
	bool IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const;

	// Is the index buffer dynamic?
	bool IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const;

	// Returns the vertex size 
	int VertexFormatSize( VertexFormat_t vertexFormat ) const
	{
		return CVertexBufferBase::VertexFormatSize( vertexFormat );
	}

	// Computes the vertex buffer pointers 
	void ComputeVertexDescription( unsigned char *pBuffer, 
		VertexFormat_t vertexFormat, MeshDesc_t &desc ) const;

	// Returns the number of buffers...
	int BufferCount() const
	{
#ifdef _DEBUG
		return CVertexBuffer::BufferCount() + CIndexBuffer::BufferCount();
#else
		return 0;
#endif
	}

	CVertexBuffer *GetVertexIDBuffer();

	IVertexBuffer *GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered = true );
	IIndexBuffer *GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered = true );
	virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords );

	int UnusedVertexFields() const { return m_nUnusedVertexFields; }
	int UnusedTextureCoords() const { return m_nUnusedTextureCoords; }

	IDirect3DVertexBuffer9 *GetZeroVertexBuffer() const { return m_pZeroVertexBuffer; }

private:
	void SetVertexIDStreamState( );
	void SetColorStreamState( );
	void SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride );
	void SetIndexStreamState( int firstVertexIdx );
	bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride );

	struct VertexBufferLookup_t
	{
		CVertexBuffer*	m_pBuffer;
		int				m_VertexSize;
	};

	void CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, CMeshDX8 *pSrcIndexMesh );

	// Cleans up the class
	void CleanUp();

	// Creates, destroys the dynamic index
	void CreateDynamicIndexBuffer();
	void DestroyDynamicIndexBuffer();

	// Creates, destroys the vertexID buffer
	void CreateVertexIDBuffer();
	void DestroyVertexIDBuffer();
	void CreateZeroVertexBuffer();
	void DestroyZeroVertexBuffer();

	// Fills a vertexID buffer
	void FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount );

	// The dynamic index buffer
	CIndexBuffer *m_pDynamicIndexBuffer;

	// A static vertexID buffer
	CVertexBuffer *m_pVertexIDBuffer;

	// The dynamic vertex buffers
	CUtlVector< VertexBufferLookup_t >	m_DynamicVertexBuffers;

	// The buffered mesh
	CBufferedMeshDX8 m_BufferedMesh;

	// The current dynamic mesh
	CDynamicMeshDX8 m_DynamicMesh;
	CDynamicMeshDX8 m_DynamicFlexMesh;

	// The current dynamic vertex buffer
	CVertexBufferDx8 m_DynamicVertexBuffer;

	// The current dynamic index buffer
	CIndexBufferDx8 m_DynamicIndexBuffer;

	// The dynamic mesh temp version (for shaders that modify vertex data)
	CTempMeshDX8 m_DynamicTempMesh;

	// Am I buffering or not?
	bool m_BufferedMode;

	// Using fat vertices?
	bool m_bUseFatVertices;

	CVertexBufferDx8 *m_pCurrentVertexBuffer;
	VertexFormat_t m_CurrentVertexFormat;
	int m_pVertexBufferOffset[MAX_DX8_STREAMS];
	int m_pCurrentVertexStride[MAX_DX8_STREAMS];
	int m_pFirstVertex[MAX_DX8_STREAMS];
	int m_pVertexCount[MAX_DX8_STREAMS];
	CIndexBufferBase *m_pCurrentIndexBuffer;
	int m_nIndexBufferOffset;
	MaterialPrimitiveType_t m_PrimitiveType;
	int m_nFirstIndex;
	int m_nNumIndices;

	unsigned int m_nUnusedVertexFields;
	unsigned int m_nUnusedTextureCoords;

	// 4096 byte static VB containing all-zeros
	IDirect3DVertexBuffer9 *m_pZeroVertexBuffer;
};

//-----------------------------------------------------------------------------
// Singleton...
//-----------------------------------------------------------------------------
static CMeshMgr g_MeshMgr;
IMeshMgr* MeshMgr()
{
	return &g_MeshMgr;
}

//-----------------------------------------------------------------------------
// Tracks stream state and queued data
//-----------------------------------------------------------------------------
static CIndexBuffer *g_pLastIndex = NULL;
static IDirect3DIndexBuffer9 *g_pLastIndexBuffer = NULL;
static CVertexBuffer *g_pLastVertex = NULL;
static IDirect3DVertexBuffer9 *g_pLastVertexBuffer = NULL;
static int g_nLastVertOffsetInBytes = 0;
static int g_nLastVertStride = 0;
static int g_LastVertexIdx = -1;
static CMeshDX8 *g_pLastColorMesh = NULL;
static int g_nLastColorMeshVertOffsetInBytes = 0;
static bool g_bUsingVertexID = false;
static bool g_bFlexMeshStreamSet = false;
static VertexFormat_t g_LastVertexFormat = 0;

inline void D3DSetStreamSource( unsigned int streamNumber, IDirect3DVertexBuffer9 *pStreamData,
								unsigned int nVertexOffsetInBytes, unsigned int stride )
{
	Dx9Device()->SetStreamSource( streamNumber, pStreamData, nVertexOffsetInBytes, stride );
}


//-----------------------------------------------------------------------------
// Tracks stream state and queued data
//-----------------------------------------------------------------------------
void Unbind( IDirect3DIndexBuffer9 *pIndexBuffer )
{
#ifdef _X360
	IDirect3DIndexBuffer9 *pBoundBuffer;
	Dx9Device()->GetIndices( &pBoundBuffer );
	if ( pBoundBuffer == pIndexBuffer )
	{
		// xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices
		Dx9Device()->SetIndices( NULL );
		g_pLastIndex = NULL;
		g_pLastIndexBuffer = NULL;
	}

	if ( pBoundBuffer )
	{
		pBoundBuffer->Release();
	}
#endif
}

void Unbind( IDirect3DVertexBuffer9 *pVertexBuffer )
{
#ifdef _X360
	UINT nOffset, nStride;
	IDirect3DVertexBuffer9 *pBoundBuffer;
	for ( int i = 0; i < MAX_DX8_STREAMS; ++i )
	{
		Dx9Device()->GetStreamSource( i, &pBoundBuffer, &nOffset, &nStride );
		if ( pBoundBuffer == pVertexBuffer )
		{
			// xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices
			Dx9Device()->SetStreamSource( i, 0, 0, 0 );
			switch ( i )
			{
			case 0:
				g_pLastVertex = NULL;
				g_pLastVertexBuffer = NULL;
				break;

			case 1:
				g_pLastColorMesh = NULL;
				g_nLastColorMeshVertOffsetInBytes = 0;
				break;
			}
		}

		if ( pBoundBuffer )
		{
			pBoundBuffer->Release();
		}
	}
#endif
}


//-----------------------------------------------------------------------------
// Helpers to count texture coordinates
//-----------------------------------------------------------------------------
static int NumTextureCoordinates( VertexFormat_t vertexFormat )
{
	int nTexCoordCount = 0;
	for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i )
	{
		if ( TexCoordSize( i, vertexFormat ) == 0 )
			continue;
		++nTexCoordCount;
	}
	return nTexCoordCount;
}


//-----------------------------------------------------------------------------
// Makes sure that the render state is always set next time
//-----------------------------------------------------------------------------
static void ResetMeshRenderState()
{
	SafeRelease( &g_pLastIndex );
	g_pLastIndexBuffer = 0;
	g_pLastVertex = 0;
	g_nLastVertOffsetInBytes = 0;
	g_pLastColorMesh = 0;
	g_nLastColorMeshVertOffsetInBytes = 0;
	g_LastVertexIdx = -1;
	g_bUsingVertexID = false;
	g_bFlexMeshStreamSet = false;
	g_LastVertexFormat = 0;
}

//-----------------------------------------------------------------------------
// Makes sure that the render state is always set next time
//-----------------------------------------------------------------------------
static void ResetIndexBufferRenderState()
{
	SafeRelease( &g_pLastIndex );
	g_pLastIndexBuffer = 0;
	g_LastVertexIdx = -1;
}


//-----------------------------------------------------------------------------
//
// Index Buffer implementations begin here
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
#ifdef _DEBUG
int CIndexBufferDx8::s_nBufferCount = 0;
#endif


//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CIndexBufferDx8::CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ) :
	BaseClass( pBudgetGroupName )
{
//	Debugger();
	
	Assert( nIndexCount != 0 );

	// NOTE: MATERIAL_INDEX_FORMAT_UNKNOWN can't be dealt with under dx9
	// because format is bound at buffer creation time. What we'll do 
	// is just arbitrarily choose to use a 16-bit index buffer of the same size	
	if ( fmt == MATERIAL_INDEX_FORMAT_UNKNOWN )
	{
		fmt = MATERIAL_INDEX_FORMAT_16BIT;
		nIndexCount /= 2;
	}

	m_pIndexBuffer = NULL;
	m_IndexFormat = fmt;
	m_nBufferSize = nIndexCount * IndexSize();
	m_nIndexCount = nIndexCount;
	m_nFirstUnwrittenOffset = 0;
	m_bIsLocked = false;
	m_bIsDynamic = IsDynamicBufferType( bufferType );
	m_bFlush = false;

#ifdef CHECK_INDICES
	m_pShadowIndices = NULL;
#endif

#ifdef VPROF_ENABLED
	m_nVProfFrame = -1;
#endif
}

CIndexBufferDx8::~CIndexBufferDx8()
{
	Free();
}


//-----------------------------------------------------------------------------
// Returns the index size
//-----------------------------------------------------------------------------
inline int CIndexBufferDx8::IndexSize() const
{
	Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN );
	return ( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT ) ? 2 : 4;
}


//-----------------------------------------------------------------------------
// Creates, destroys the index buffer
//-----------------------------------------------------------------------------
bool CIndexBufferDx8::Allocate()
{
#ifdef OSX
	Debugger();
#endif
	Assert( !m_pIndexBuffer );
	m_nFirstUnwrittenOffset = 0;

	// FIXME: This doesn't really work for dynamic buffers; dynamic buffers
	// can't have mixed-type indices in them. Bleah.
	D3DFORMAT format = ( m_IndexFormat == MATERIAL_INDEX_FORMAT_32BIT ) ? 
		D3DFMT_INDEX32 : D3DFMT_INDEX16;

	DWORD usage = D3DUSAGE_WRITEONLY;
	if ( m_bIsDynamic )
	{
		usage |= D3DUSAGE_DYNAMIC;
	}

	HRESULT hr = Dx9Device()->CreateIndexBuffer( 
		m_nBufferSize, usage, format, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL );

#if !defined( _X360 )
	if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) )
	{
		// Don't have the memory for this.  Try flushing all managed resources
		// out of vid mem and try again.
		// FIXME: need to record this
		Dx9Device()->EvictManagedResources();
		hr = Dx9Device()->CreateIndexBuffer( 
			m_nBufferSize, usage, format, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL );
	}
#endif // !X360

	if ( FAILED(hr) || ( m_pIndexBuffer == NULL ) )
	{
		Warning( "CIndexBufferDx8::Allocate: CreateIndexBuffer failed!\n" );
		return false;
	}

	if ( !m_bIsDynamic )
	{
		VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, 
			COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize );
	}
	else
	{
		VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, 
			COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize );
	}

#ifdef CHECK_INDICES
	Assert ( !m_pShadowIndices );
	m_pShadowIndices = new unsigned char[ m_nBufferSize ];
	memset( m_pShadowIndices, 0xFF, m_nBufferSize );
#endif // CHECK_INDICES

#ifdef _DEBUG
	++s_nBufferCount;
#endif

	return true;
}

void CIndexBufferDx8::Free()
{
// FIXME:	Unlock(0);
	if ( m_pIndexBuffer )
	{
#ifdef _DEBUG
		--s_nBufferCount;
#endif

		m_pIndexBuffer->Release();
		m_pIndexBuffer = NULL;

		if ( !m_bIsDynamic )
		{
			VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, 
				COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize );
		}
		else
		{
			VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, 
				COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize );
		}
	}

#ifdef CHECK_INDICES
	if ( m_pShadowIndices )
	{
		delete[] m_pShadowIndices;
		m_pShadowIndices = NULL;
	}
#endif // CHECK_INDICES
}


//-----------------------------------------------------------------------------
// Index buffer information
//-----------------------------------------------------------------------------
int CIndexBufferDx8::IndexCount( ) const
{
	Assert( !m_bIsDynamic );
	return m_nIndexCount;
}

MaterialIndexFormat_t CIndexBufferDx8::IndexFormat() const
{
	Assert( !m_bIsDynamic );
	return m_IndexFormat;
}


//-----------------------------------------------------------------------------
// Returns true if the buffer is dynamic
//-----------------------------------------------------------------------------
bool CIndexBufferDx8::IsDynamic() const
{
	return m_bIsDynamic;
}


//-----------------------------------------------------------------------------
// Only used by dynamic buffers, indicates the next lock should perform a discard.
//-----------------------------------------------------------------------------
void CIndexBufferDx8::Flush()
{
	// This strange-looking line makes a flush only occur if the buffer is dynamic.
	m_bFlush = m_bIsDynamic;
}


//-----------------------------------------------------------------------------
// Returns the D3D buffer
//-----------------------------------------------------------------------------
IDirect3DIndexBuffer9* CIndexBufferDx8::GetDx9Buffer()
{
	return m_pIndexBuffer;
}


//-----------------------------------------------------------------------------
// Returns a shadowed index, for validation
//-----------------------------------------------------------------------------
#ifdef CHECK_INDICES
unsigned short CIndexBufferDx8::GetShadowIndex( int i ) const
{
	Assert( i >= 0 && i < m_nIndexCount );
	Assert( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT );
	return *(unsigned short*)( &m_pShadowIndices[ i * IndexSize() ] );
}
#endif // CHECK_INDICES


//-----------------------------------------------------------------------------
// Used to measure how much static buffer memory is touched each frame 
//-----------------------------------------------------------------------------
void CIndexBufferDx8::HandlePerFrameTextureStats( int nFrame )
{
#ifdef VPROF_ENABLED
	if ( m_nVProfFrame != nFrame && !m_bIsDynamic )
	{
		m_nVProfFrame = nFrame;
		VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, 
			COUNTER_GROUP_TEXTURE_PER_FRAME, m_nBufferSize );
	}
#endif
}


//-----------------------------------------------------------------------------
// Casts a dynamic buffer to be a particular index type
//-----------------------------------------------------------------------------
void CIndexBufferDx8::BeginCastBuffer( MaterialIndexFormat_t format )
{
	// NOTE: This should have no effect under Dx9, since we can't recast index buffers.
	Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN );
	Assert( m_bIsDynamic && ( m_IndexFormat == format ) );
}

void CIndexBufferDx8::EndCastBuffer( )
{
	// NOTE: This should have no effect under Dx9, since we can't recast index buffers.
}

int CIndexBufferDx8::GetRoomRemaining() const
{ 
	return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / IndexSize();
}


//-----------------------------------------------------------------------------
// Locks/unlocks the index buffer
//-----------------------------------------------------------------------------
bool CIndexBufferDx8::Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc )
{
	Assert( !m_bIsLocked && ( nMaxIndexCount != 0 ) && ( nMaxIndexCount <= m_nIndexCount ) );
	Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN );

	// FIXME: Why do we need to sync matrices now?
	ShaderUtil()->SyncMatrices();
	g_ShaderMutex.Lock();

	VPROF( "CIndexBufferX8::Lock" );		

	void *pLockedData = NULL;
	HRESULT hr;
	int nMemoryRequired;
	bool bHasEnoughMemory;
	UINT nLockFlags;

	// This can happen if the buffer was locked but a type wasn't bound
	if ( m_IndexFormat == MATERIAL_INDEX_FORMAT_UNKNOWN )
		goto indexBufferLockFailed;

	// Just give the app crap buffers to fill up while we're suppressed...
	if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxIndexCount == 0 ) )
		goto indexBufferLockFailed;

	// Did we ask for something too large?
	if ( nMaxIndexCount > m_nIndexCount )
	{
		Warning( "Too many indices for index buffer. . tell a programmer (%d>%d)\n", nMaxIndexCount, m_nIndexCount );
		goto indexBufferLockFailed;
	}

	// We might not have a buffer owing to alt-tab type stuff
	if ( !m_pIndexBuffer )
	{
		if ( !Allocate() )
			goto indexBufferLockFailed;
	}

	// Check to see if we have enough memory 
	nMemoryRequired = nMaxIndexCount * IndexSize();
	bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize );

	nLockFlags = D3DLOCK_NOSYSLOCK;
	if ( bAppend )
	{
		// Can't have the first lock after a flush be an appending lock
		Assert( !m_bFlush );

		// If we're appending and we don't have enough room, then puke!
		if ( !bHasEnoughMemory || m_bFlush )
			goto indexBufferLockFailed;
		nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
	}
	else
	{
		// If we're not appending, no overwrite unless we don't have enough room
		// If we're a static buffer, always discard if we're not appending
		if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic )
		{
			nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
		}
		else
		{
			if ( m_bIsDynamic )
			{
				nLockFlags |= D3DLOCK_DISCARD;
			}
			m_nFirstUnwrittenOffset = 0;
			m_bFlush = false;
		}
	}

#if !defined( _X360 )
	hr = m_pIndexBuffer->Lock(  m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags );
#else
	hr = m_pIndexBuffer->Lock( 0, 0, &pLockedData, nLockFlags );
	pLockedData = ( ( unsigned char * )pLockedData + m_nFirstUnwrittenOffset );
#endif

	if ( FAILED( hr ) )
	{
		Warning( "Failed to lock index buffer in CIndexBufferDx8::LockIndexBuffer\n" );
		goto indexBufferLockFailed;
	}

	desc.m_pIndices = (unsigned short*)( pLockedData );
	desc.m_nIndexSize = IndexSize() >> 1;
	if ( g_pHardwareConfig->SupportsStreamOffset() )
	{
		desc.m_nFirstIndex = 0;
		desc.m_nOffset = m_nFirstUnwrittenOffset;
	}
	else
	{
		desc.m_nFirstIndex = m_nFirstUnwrittenOffset / IndexSize();
		Assert( (int)( desc.m_nFirstIndex * IndexSize() ) == m_nFirstUnwrittenOffset );
		desc.m_nOffset = 0;
	}
	m_bIsLocked = true;

#ifdef CHECK_INDICES
	m_nLockIndexBufferSize = nMemoryRequired;
	m_pLockIndexBuffer = desc.m_pIndices;
	m_nLockIndexOffset = m_nFirstUnwrittenOffset;
#endif // CHECK_INDICES

	return true;

indexBufferLockFailed:
	g_ShaderMutex.Unlock();

	// Set up a bogus index descriptor
	desc.m_pIndices = g_nScratchIndexBuffer;
	desc.m_nIndexSize = 0;
	desc.m_nFirstIndex = 0;
	desc.m_nOffset = 0;
	return false;
}

void CIndexBufferDx8::Unlock( int nWrittenIndexCount, IndexDesc_t &desc )
{
	Assert( nWrittenIndexCount <= m_nIndexCount );

	// NOTE: This can happen if another application finishes
	// initializing during the construction of a mesh
	if ( !m_bIsLocked )
		return;

#ifdef CHECK_INDICES
	memcpy( (unsigned char*)m_pShadowIndices + m_nLockIndexOffset, m_pLockIndexBuffer, nWrittenIndexCount * IndexSize() );
#endif // CHECK_INDICES

	if ( m_pIndexBuffer )
	{
		m_pIndexBuffer->Unlock();
	}

	m_nFirstUnwrittenOffset += nWrittenIndexCount * IndexSize();
	m_bIsLocked = false;
	g_ShaderMutex.Unlock();
}


//-----------------------------------------------------------------------------
//
// Vertex Buffer implementations begin here
//
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
// globals
//-----------------------------------------------------------------------------
#ifdef _DEBUG
int CVertexBufferDx8::s_nBufferCount = 0;
#endif


//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CVertexBufferDx8::CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ) : 
	BaseClass( pBudgetGroupName )
{
//	Debugger();
	Assert( nVertexCount != 0 );

	m_pVertexBuffer = NULL;
	m_VertexFormat = fmt;
	m_nVertexCount = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? 0 : nVertexCount;
	m_nBufferSize = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? nVertexCount : nVertexCount * VertexSize();
	m_nFirstUnwrittenOffset = 0;
	m_bIsLocked = false;
	m_bIsDynamic = ( type == SHADER_BUFFER_TYPE_DYNAMIC ) || ( type == SHADER_BUFFER_TYPE_DYNAMIC_TEMP );
	m_bFlush = false;

#ifdef VPROF_ENABLED
	if ( !m_bIsDynamic )
	{
		char name[256];
		V_strcpy_safe( name, "TexGroup_global_" );
		V_strcat_safe( name, pBudgetGroupName, sizeof(name) );
		m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_GLOBAL );

		V_strcpy_safe( name, "TexGroup_frame_" );
		V_strcat_safe( name, pBudgetGroupName, sizeof(name) );
		m_pFrameCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_PER_FRAME );
	}
	else
	{
		m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER, COUNTER_GROUP_TEXTURE_GLOBAL );
		m_pFrameCounter = NULL;
	}
	m_nVProfFrame = -1;
#endif
}

CVertexBufferDx8::~CVertexBufferDx8()
{
	Free();
}


//-----------------------------------------------------------------------------
// Returns the vertex size
//-----------------------------------------------------------------------------
inline int CVertexBufferDx8::VertexSize() const
{
	Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN );
	return VertexFormatSize( m_VertexFormat );
}

//-----------------------------------------------------------------------------
// Creates, destroys the vertex buffer
//-----------------------------------------------------------------------------
bool CVertexBufferDx8::Allocate()
{
	Assert( !m_pVertexBuffer );
	m_nFirstUnwrittenOffset = 0;

	D3DPOOL pool = D3DPOOL_MANAGED;

#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9)
	extern bool g_ShaderDeviceUsingD3D9Ex;
	if ( g_ShaderDeviceUsingD3D9Ex )
	{
		pool = D3DPOOL_DEFAULT;
	}
#endif

	DWORD usage = D3DUSAGE_WRITEONLY;
	if ( m_bIsDynamic )
	{
		usage |= D3DUSAGE_DYNAMIC;
		pool = D3DPOOL_DEFAULT;
		// Dynamic meshes should never be compressed (slows down writing to them)
		Assert( CompressionType( GetVertexFormat() ) == VERTEX_COMPRESSION_NONE );
	}

	HRESULT hr = Dx9Device()->CreateVertexBuffer( 
		m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL );

#if !defined( _X360 )
	if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) )
	{
		// Don't have the memory for this.  Try flushing all managed resources
		// out of vid mem and try again.
		// FIXME: need to record this
		Dx9Device()->EvictManagedResources();
		hr = Dx9Device()->CreateVertexBuffer( 
			m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL );
	}
#endif // !X360

	if ( FAILED(hr) || ( m_pVertexBuffer == NULL ) )
	{
		Warning( "CVertexBufferDx8::Allocate: CreateVertexBuffer failed!\n" );
		return false;
	}

	// Track VB allocations
	g_VBAllocTracker->CountVB( m_pVertexBuffer, m_bIsDynamic, m_nBufferSize, VertexSize(), GetVertexFormat() );

#ifdef VPROF_ENABLED
	if ( IsX360() || !m_bIsDynamic )
	{
		Assert( m_pGlobalCounter );
		*m_pGlobalCounter += m_nBufferSize;
	}
#endif

#ifdef _DEBUG
		++s_nBufferCount;
#endif

	return true;
}

void CVertexBufferDx8::Free()
{
	// FIXME:	Unlock(0);
	if ( m_pVertexBuffer )
	{
#ifdef _DEBUG
		--s_nBufferCount;
#endif

	// Track VB allocations
	g_VBAllocTracker->UnCountVB( m_pVertexBuffer );

#ifdef VPROF_ENABLED
		if ( IsX360() || !m_bIsDynamic )
		{
			Assert( m_pGlobalCounter );
			*m_pGlobalCounter -= m_nBufferSize;
		}
#endif

		m_pVertexBuffer->Release();
		m_pVertexBuffer = NULL;
	}
}


//-----------------------------------------------------------------------------
// Vertex buffer information
//-----------------------------------------------------------------------------
int CVertexBufferDx8::VertexCount() const
{
	Assert( !m_bIsDynamic );
	return m_nVertexCount;
}

VertexFormat_t CVertexBufferDx8::GetVertexFormat() const
{
	Assert( !m_bIsDynamic );
	return m_VertexFormat;
}


//-----------------------------------------------------------------------------
// Returns true if the buffer is dynamic
//-----------------------------------------------------------------------------
bool CVertexBufferDx8::IsDynamic() const
{
	return m_bIsDynamic;
}


//-----------------------------------------------------------------------------
// Only used by dynamic buffers, indicates the next lock should perform a discard.
//-----------------------------------------------------------------------------
void CVertexBufferDx8::Flush()
{
	// This strange-looking line makes a flush only occur if the buffer is dynamic.
	m_bFlush = m_bIsDynamic;
}


//-----------------------------------------------------------------------------
// Returns the D3D buffer
//-----------------------------------------------------------------------------
IDirect3DVertexBuffer9* CVertexBufferDx8::GetDx9Buffer()
{
	return m_pVertexBuffer;
}


//-----------------------------------------------------------------------------
// Casts a dynamic buffer to be a particular vertex type
//-----------------------------------------------------------------------------
void CVertexBufferDx8::BeginCastBuffer( VertexFormat_t format )
{
	Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN );
	Assert( m_bIsDynamic && ( m_VertexFormat == 0 || m_VertexFormat == format ) );
	if ( !m_bIsDynamic )
		return;

	m_VertexFormat = format;
	int nVertexSize = VertexSize();
	m_nVertexCount = m_nBufferSize / nVertexSize;

	// snap current position up to the next position based on expected size
	// so append can safely guarantee nooverwrite regardless of a format growth or shrinkage 
	if ( !g_pHardwareConfig->SupportsStreamOffset() )
	{
		m_nFirstUnwrittenOffset = ( m_nFirstUnwrittenOffset + nVertexSize - 1 ) / nVertexSize;
		m_nFirstUnwrittenOffset *= nVertexSize;
		if ( m_nFirstUnwrittenOffset > m_nBufferSize )
		{
			m_nFirstUnwrittenOffset = m_nBufferSize;
		}
	}
}

void CVertexBufferDx8::EndCastBuffer( )
{
	Assert( m_bIsDynamic && m_VertexFormat != 0 );
	if ( !m_bIsDynamic )
		return;
	m_VertexFormat = 0;
	m_nVertexCount = 0;
}


//-----------------------------------------------------------------------------
// Returns the number of vertices we can still write into the buffer
//-----------------------------------------------------------------------------
int CVertexBufferDx8::GetRoomRemaining() const
{ 
	return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / VertexSize();
}


//-----------------------------------------------------------------------------
// Locks/unlocks the vertex buffer mesh
//-----------------------------------------------------------------------------
bool CVertexBufferDx8::Lock( int nMaxVertexCount, bool bAppend, VertexDesc_t &desc )
{
	Assert( !m_bIsLocked && ( nMaxVertexCount != 0 ) && ( nMaxVertexCount <= m_nVertexCount ) );
	Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN );

	// FIXME: Why do we need to sync matrices now?
	ShaderUtil()->SyncMatrices();
	g_ShaderMutex.Lock();

	VPROF( "CVertexBufferDx8::Lock" );		

	void *pLockedData = NULL;
	HRESULT hr;
	int nMemoryRequired;
	bool bHasEnoughMemory;
	UINT nLockFlags;

	// This can happen if the buffer was locked but a type wasn't bound
	if ( m_VertexFormat == VERTEX_FORMAT_UNKNOWN )
		goto vertexBufferLockFailed;

	// Just give the app crap buffers to fill up while we're suppressed...
	if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxVertexCount == 0 ) )
		goto vertexBufferLockFailed;

	// Did we ask for something too large?
	if ( nMaxVertexCount > m_nVertexCount )
	{
		Warning( "Too many vertices for vertex buffer. . tell a programmer (%d>%d)\n", nMaxVertexCount, m_nVertexCount );
		goto vertexBufferLockFailed;
	}

	// We might not have a buffer owing to alt-tab type stuff
	if ( !m_pVertexBuffer )
	{
		if ( !Allocate() )
			goto vertexBufferLockFailed;
	}

	// Check to see if we have enough memory 
	nMemoryRequired = nMaxVertexCount * VertexSize();
	bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize );

	nLockFlags = D3DLOCK_NOSYSLOCK;
	if ( bAppend )
	{
		// Can't have the first lock after a flush be an appending lock
		Assert( !m_bFlush );

		// If we're appending and we don't have enough room, then puke!
		if ( !bHasEnoughMemory || m_bFlush )
			goto vertexBufferLockFailed;
		nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
	}
	else
	{
		// If we're not appending, no overwrite unless we don't have enough room
		// If we're a static buffer, always discard if we're not appending
		if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic )
		{
			nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
		}
		else
		{
			if ( m_bIsDynamic )
			{
				nLockFlags |= D3DLOCK_DISCARD;
			}
			m_nFirstUnwrittenOffset = 0;
			m_bFlush = false;
		}
	}

#if !defined( _X360 )
	hr = m_pVertexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags );
#else
	hr = m_pVertexBuffer->Lock( 0, 0, &pLockedData, nLockFlags );
	pLockedData = (unsigned char*)pLockedData + m_nFirstUnwrittenOffset;
#endif

	if ( FAILED( hr ) )
	{
		// Check if paged pool is in critical state ( < 5% free )
		PAGED_POOL_INFO_t ppi;
		if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
			( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
		{
			Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" );
		}
		else
		{
			Warning( "Failed to lock vertex buffer in CVertexBufferDx8::Lock\n" );
		}
		goto vertexBufferLockFailed;
	}

	ComputeVertexDescription( (unsigned char*)pLockedData, m_VertexFormat, desc );
	if ( g_pHardwareConfig->SupportsStreamOffset() )
	{
		desc.m_nFirstVertex = 0;
		desc.m_nOffset = m_nFirstUnwrittenOffset;
	}
	else
	{
		desc.m_nFirstVertex = m_nFirstUnwrittenOffset / VertexSize();
		desc.m_nOffset = 0;
		Assert( m_nFirstUnwrittenOffset == VertexSize() * desc.m_nFirstVertex );
	}
	m_bIsLocked = true;
	return true;

vertexBufferLockFailed:
	ComputeVertexDescription( 0, 0, desc );
	desc.m_nFirstVertex = 0;
	desc.m_nOffset = 0; 
	return false;
}


void CVertexBufferDx8::Unlock( int nWrittenVertexCount, VertexDesc_t &desc )
{
	Assert( nWrittenVertexCount <= m_nVertexCount );

	// NOTE: This can happen if another application finishes
	// initializing during the construction of a mesh
	if ( !m_bIsLocked )
		return;

	if ( m_pVertexBuffer )
	{
		m_pVertexBuffer->Unlock();
	}

	m_nFirstUnwrittenOffset += nWrittenVertexCount * VertexSize();
	m_bIsLocked = false;
	g_ShaderMutex.Unlock();
}


//-----------------------------------------------------------------------------
// Used to measure how much static buffer memory is touched each frame 
//-----------------------------------------------------------------------------
void CVertexBufferDx8::HandlePerFrameTextureStats( int nFrame )
{
#ifdef VPROF_ENABLED
	if ( m_nVProfFrame != nFrame && !m_bIsDynamic )
	{
		m_nVProfFrame = nFrame;
		m_pFrameCounter += m_nBufferSize;
	}
#endif
}


//-----------------------------------------------------------------------------
// Helpers with meshdescs...
//-----------------------------------------------------------------------------
// FIXME: add compression-agnostic read-accessors (which decompress and return by value, checking desc.m_CompressionType)
inline D3DXVECTOR3 &Position( MeshDesc_t const &desc, int vert )
{
	return *(D3DXVECTOR3*)((unsigned char*)desc.m_pPosition + vert * desc.m_VertexSize_Position );
}

inline float Wrinkle( MeshDesc_t const &desc, int vert )
{
	return *(float*)((unsigned char*)desc.m_pWrinkle + vert * desc.m_VertexSize_Wrinkle );
}

inline D3DXVECTOR3 &BoneWeight( MeshDesc_t const &desc, int vert )
{
	Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE );
	return *(D3DXVECTOR3*)((unsigned char*)desc.m_pBoneWeight + vert * desc.m_VertexSize_BoneWeight );
}

inline unsigned char *BoneIndex( MeshDesc_t const &desc, int vert )
{
	return desc.m_pBoneMatrixIndex + vert * desc.m_VertexSize_BoneMatrixIndex;
}

inline D3DXVECTOR3 &Normal( MeshDesc_t const &desc, int vert )
{
	Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE );
	return *(D3DXVECTOR3*)((unsigned char*)desc.m_pNormal + vert * desc.m_VertexSize_Normal );
}

inline unsigned char *Color( MeshDesc_t const &desc, int vert )
{
	return desc.m_pColor + vert * desc.m_VertexSize_Color;
}

inline D3DXVECTOR2 &TexCoord( MeshDesc_t const &desc, int vert, int stage )
{
	return *(D3DXVECTOR2*)((unsigned char*)desc.m_pTexCoord[stage] + vert * desc.m_VertexSize_TexCoord[stage] );
}

inline D3DXVECTOR3 &TangentS( MeshDesc_t const &desc, int vert )
{
	return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentS + vert * desc.m_VertexSize_TangentS );
}

inline D3DXVECTOR3 &TangentT( MeshDesc_t const &desc, int vert )
{
	return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentT + vert * desc.m_VertexSize_TangentT );
}


//-----------------------------------------------------------------------------
//
// Base mesh
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------

CBaseMeshDX8::CBaseMeshDX8() : m_VertexFormat(0)
{
	m_bMeshLocked = false;
#ifdef DBGFLAG_ASSERT
	m_IsDrawing = false;
	m_pMaterial = 0;
#endif
}

CBaseMeshDX8::~CBaseMeshDX8()
{			   
}


//-----------------------------------------------------------------------------
// For debugging...
//-----------------------------------------------------------------------------
bool CBaseMeshDX8::DebugTrace() const
{
#ifdef _DEBUG
	if (m_pMaterial)
		return m_pMaterial->PerformDebugTrace();
#endif

	return false;
}

void CBaseMeshDX8::SetMaterial( IMaterial *pMaterial )
{
#ifdef DBGFLAG_ASSERT
	m_pMaterial = static_cast<IMaterialInternal *>(pMaterial);
#endif
}


//-----------------------------------------------------------------------------
// Sets, gets the vertex format
//-----------------------------------------------------------------------------
void CBaseMeshDX8::SetVertexFormat( VertexFormat_t format )
{
	m_VertexFormat = format;
}

VertexFormat_t CBaseMeshDX8::GetVertexFormat() const
{
	return m_VertexFormat;
}


//-----------------------------------------------------------------------------
// Sets/gets the morph format
//-----------------------------------------------------------------------------
void CBaseMeshDX8::SetMorphFormat( MorphFormat_t format )
{
	m_MorphFormat = format;
}

MorphFormat_t CBaseMeshDX8::GetMorphFormat() const
{
	return m_MorphFormat;
}


//-----------------------------------------------------------------------------
// Am I using morph data?
//-----------------------------------------------------------------------------
bool CBaseMeshDX8::IsUsingMorphData() const
{
	LOCK_SHADERAPI();
	// We're not using a morph unless the bound morph is a superset of what the rendermesh needs
	MorphFormat_t morphFormat = GetMorphFormat();
	if ( !morphFormat )
		return false;

	return ( ( morphFormat & ShaderUtil()->GetBoundMorphFormat() ) == morphFormat );
}

//-----------------------------------------------------------------------------
// Do I need to reset the vertex format?
//-----------------------------------------------------------------------------
bool CBaseMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const
{
	return m_VertexFormat != fmt;
}


//-----------------------------------------------------------------------------
// Do I have enough room?
//-----------------------------------------------------------------------------
bool CBaseMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const
{
	// by default, we do
	return true;
}

//-----------------------------------------------------------------------------
// Estimate the memory used
//-----------------------------------------------------------------------------
unsigned CBaseMeshDX8::ComputeMemoryUsed()
{
	unsigned size = 0;

	if ( GetVertexBuffer() )
	{
		size += GetVertexBuffer()->VertexCount() * GetVertexBuffer()->VertexSize();
	}

	if ( GetIndexBuffer() )
	{
		size += GetIndexBuffer()->IndexCount() * GetIndexBuffer()->IndexSize();
	}

	return size;
}


//-----------------------------------------------------------------------------
// Locks mesh for modifying
//-----------------------------------------------------------------------------
void CBaseMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
{
	LOCK_SHADERAPI();
	// for the time being, disallow for most cases
	Assert(0);
}

void CBaseMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
{
	LOCK_SHADERAPI();
	// for the time being, disallow for most cases
	Assert(0);
}

void CBaseMeshDX8::ModifyEnd( MeshDesc_t& desc )
{
	LOCK_SHADERAPI();
	// for the time being, disallow for most cases
	Assert(0);
}


//-----------------------------------------------------------------------------
// Begins a pass
//-----------------------------------------------------------------------------
void CBaseMeshDX8::BeginPass( )
{
	LOCK_SHADERAPI();
}


//-----------------------------------------------------------------------------
// Sets the render state and gets the drawing going
//-----------------------------------------------------------------------------
inline void CBaseMeshDX8::DrawMesh( )
{
#ifdef DBGFLAG_ASSERT
	// Make sure we're not drawing...
	Assert( !m_IsDrawing );
	m_IsDrawing = true;
#endif

	// This is going to cause RenderPass to get called a bunch
	ShaderAPI()->DrawMesh( this );

#ifdef DBGFLAG_ASSERT
	m_IsDrawing = false;
#endif
}


//-----------------------------------------------------------------------------
// Spews the mesh data
//-----------------------------------------------------------------------------
void CBaseMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc )
{
	LOCK_SHADERAPI();
	// This has regressed.
	int i;


	// FIXME: just fall back to the base class (CVertexBufferBase) version of this function!


#ifdef _DEBUG
	if( m_pMaterial )
	{
		Plat_DebugString( ( const char * )m_pMaterial->GetName() );
		Plat_DebugString( "\n" );
	}
#endif // _DEBUG
	
	// This is needed so buffering can just use this
	VertexFormat_t fmt = m_VertexFormat;

	// Set up the vertex descriptor
	MeshDesc_t desc = spewDesc;

	char tempbuf[256];
	char* temp = tempbuf;
	sprintf( tempbuf,"\nVerts: (Vertex Format %llx)\n", fmt);
	Plat_DebugString(tempbuf);

	CVertexBufferBase::PrintVertexFormat( fmt );

	int numBoneWeights = NumBoneWeights( fmt );
	for ( i = 0; i < nVertexCount; ++i )
	{
		temp += sprintf( temp, "[%4d] ", i + desc.m_nFirstVertex );
		if( fmt & VERTEX_POSITION )
		{
			D3DXVECTOR3& pos = Position( desc, i );
			temp += sprintf(temp, "P %8.2f %8.2f %8.2f ",
				pos[0], pos[1], pos[2]);
		}

		if ( fmt & VERTEX_WRINKLE )
		{
			float flWrinkle = Wrinkle( desc, i );
			temp += sprintf(temp, "Wr %8.2f ",flWrinkle );
		}

		if (numBoneWeights > 0)
		{
			temp += sprintf(temp, "BW ");
			float* pWeight = BoneWeight( desc, i );
			for (int j = 0; j < numBoneWeights; ++j)
			{
				temp += sprintf(temp, "%1.2f ", pWeight[j]);
			}
		}
		if ( fmt & VERTEX_BONE_INDEX )
		{
			unsigned char *pIndex = BoneIndex( desc, i );
			temp += sprintf( temp, "BI %d %d %d %d ", ( int )pIndex[0], ( int )pIndex[1], ( int )pIndex[2], ( int )pIndex[3] );
			Assert( pIndex[0] < 16 );
			Assert( pIndex[1] < 16 );
			Assert( pIndex[2] < 16 );
			Assert( pIndex[3] < 16 );
		}

		if ( fmt & VERTEX_NORMAL )
		{
			D3DXVECTOR3& normal = Normal( desc, i );
			temp += sprintf(temp, "N %1.2f %1.2f %1.2f ",
				normal[0],	normal[1],	normal[2]);
		}
		
		if (fmt & VERTEX_COLOR)
		{
			unsigned char* pColor = Color( desc, i );
			temp += sprintf(temp, "C b %3d g %3d r %3d a %3d ",
				pColor[0], pColor[1], pColor[2], pColor[3]);
		}

		for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j)
		{
			if( TexCoordSize( j, fmt ) > 0)
			{
				D3DXVECTOR2& texcoord = TexCoord( desc, i, j );
				temp += sprintf(temp, "T%d %.2f %.2f ", j,texcoord[0], texcoord[1]);
			}
		}

		if (fmt & VERTEX_TANGENT_S)
		{
			D3DXVECTOR3& tangentS = TangentS( desc, i );
			temp += sprintf(temp, "S %1.2f %1.2f %1.2f ",
				tangentS[0], tangentS[1], tangentS[2]);
		}

		if (fmt & VERTEX_TANGENT_T)
		{
			D3DXVECTOR3& tangentT = TangentT( desc, i );
			temp += sprintf(temp, "T %1.2f %1.2f %1.2f ",
				tangentT[0], tangentT[1], tangentT[2]);
		}

		sprintf(temp,"\n");
		Plat_DebugString(tempbuf);
		temp = tempbuf;
	}

	sprintf( tempbuf,"\nIndices: %d\n", nIndexCount );
	Plat_DebugString(tempbuf);
	for ( i = 0; i < nIndexCount; ++i )
	{
		temp += sprintf( temp, "%d ", ( int )desc.m_pIndices[i] );
		if ((i & 0x0F) == 0x0F)
		{
			sprintf( temp, "\n" );
			Plat_DebugString(tempbuf);
			tempbuf[0] = '\0';
			temp = tempbuf;
		}
	}
	sprintf(temp,"\n");
	Plat_DebugString( tempbuf );
}

void CBaseMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc )
{
	LOCK_SHADERAPI();
#ifdef VALIDATE_DEBUG
	int i;


	// FIXME: just fall back to the base class (CVertexBufferBase) version of this function!


	// This is needed so buffering can just use this
	VertexFormat_t fmt = m_pMaterial->GetVertexUsage();

	// Set up the vertex descriptor
	MeshDesc_t desc = spewDesc;

	int numBoneWeights = NumBoneWeights( fmt );
	for ( i = 0; i < nVertexCount; ++i )
	{
		if( fmt & VERTEX_POSITION )
		{
			D3DXVECTOR3& pos = Position( desc, i );
			Assert( IsFinite( pos[0] ) && IsFinite( pos[1] ) && IsFinite( pos[2] ) );
		}
		if( fmt & VERTEX_WRINKLE )
		{
			float flWrinkle = Wrinkle( desc, i );
			Assert( IsFinite( flWrinkle ) );
		}
		if (numBoneWeights > 0)
		{
			float* pWeight = BoneWeight( desc, i );
			for (int j = 0; j < numBoneWeights; ++j)
			{
				Assert( pWeight[j] >= 0.0f && pWeight[j] <= 1.0f );
			}
		}
		if( fmt & VERTEX_BONE_INDEX )
		{
			unsigned char *pIndex = BoneIndex( desc, i );
			Assert( pIndex[0] >= 0 && pIndex[0] < 16 );
			Assert( pIndex[1] >= 0 && pIndex[1] < 16 );
			Assert( pIndex[2] >= 0 && pIndex[2] < 16 );
			Assert( pIndex[3] >= 0 && pIndex[3] < 16 );
		}
		if( fmt & VERTEX_NORMAL )
		{
			D3DXVECTOR3& normal = Normal( desc, i );
			Assert( normal[0] >= -1.05f && normal[0] <= 1.05f );
			Assert( normal[1] >= -1.05f && normal[1] <= 1.05f );
			Assert( normal[2] >= -1.05f && normal[2] <= 1.05f );
		}
		
		if (fmt & VERTEX_COLOR)
		{
			int* pColor = (int*)Color( desc, i );
			Assert( *pColor != FLOAT32_NAN_BITS );
		}

		for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j)
		{
			if( TexCoordSize( j, fmt ) > 0)
			{
				D3DXVECTOR2& texcoord = TexCoord( desc, i, j );
				Assert( IsFinite( texcoord[0] ) && IsFinite( texcoord[1] ) );
			}
		}

		if (fmt & VERTEX_TANGENT_S)
		{
			D3DXVECTOR3& tangentS = TangentS( desc, i );
			Assert( IsFinite( tangentS[0] ) && IsFinite( tangentS[1] ) && IsFinite( tangentS[2] ) );
		}

		if (fmt & VERTEX_TANGENT_T)
		{
			D3DXVECTOR3& tangentT = TangentT( desc, i );
			Assert( IsFinite( tangentT[0] ) && IsFinite( tangentT[1] ) && IsFinite( tangentT[2] ) );
		}
	}
#endif // _DEBUG
}

void CBaseMeshDX8::Draw( CPrimList *pLists, int nLists )
{
	LOCK_SHADERAPI();
	Assert( !"CBaseMeshDX8::Draw(CPrimList, int): should never get here." );
}


// Copy verts and/or indices to a mesh builder. This only works for temp meshes!
void CBaseMeshDX8::CopyToMeshBuilder( 
	int iStartVert,		// Which vertices to copy.
	int nVerts, 
	int iStartIndex,	// Which indices to copy.
	int nIndices, 
	int indexOffset,	// This is added to each index.
	CMeshBuilder &builder )
{
	LOCK_SHADERAPI();
	Assert( false );
	Warning( "CopyToMeshBuilder called on something other than a temp mesh.\n" );
}


//-----------------------------------------------------------------------------
//
// static mesh
//
//-----------------------------------------------------------------------------

CPrimList *CMeshDX8::s_pPrims;
int CMeshDX8::s_nPrims;
unsigned int CMeshDX8::s_FirstVertex;
unsigned int CMeshDX8::s_NumVertices;


//-----------------------------------------------------------------------------
// Computes the mode
//-----------------------------------------------------------------------------
inline D3DPRIMITIVETYPE ComputeMode( MaterialPrimitiveType_t type )
{
	switch(type)
	{
#ifdef _X360
	case MATERIAL_INSTANCED_QUADS:
		return D3DPT_QUADLIST;
#endif

	case MATERIAL_POINTS:
		return D3DPT_POINTLIST;
		
	case MATERIAL_LINES:
		return D3DPT_LINELIST;

	case MATERIAL_TRIANGLES:
		return D3DPT_TRIANGLELIST;

	case MATERIAL_TRIANGLE_STRIP:
		return D3DPT_TRIANGLESTRIP;

	// Here, we expect to have the type set later. only works for static meshes
	case MATERIAL_HETEROGENOUS:
		return (D3DPRIMITIVETYPE)-1;

	default:
		Assert(0);
		return (D3DPRIMITIVETYPE)-1;
	}
}

//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CMeshDX8::CMeshDX8( const char *pTextureGroupName ) : m_NumVertices(0), m_NumIndices(0), m_pVertexBuffer(0),
	m_pColorMesh( 0 ), m_nColorMeshVertOffsetInBytes( 0 ),
	m_pIndexBuffer(0), m_Type(MATERIAL_TRIANGLES), m_IsVBLocked(false),
	m_IsIBLocked(false)
{
	m_pTextureGroupName = pTextureGroupName;
	m_Mode = ComputeMode(m_Type);

	m_bHasFlexVerts = false;
	m_pFlexVertexBuffer = NULL;
	m_nFlexVertOffsetInBytes = 0;
}

CMeshDX8::~CMeshDX8()
{
	// Don't release the vertex buffer 
	if (!g_MeshMgr.IsDynamicMesh(this))
	{
		if (m_pVertexBuffer)
		{
			delete m_pVertexBuffer;
		}
		if (m_pIndexBuffer)
		{
			SafeRelease( &m_pIndexBuffer );
		}
	}
}

void CMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes )
{
	if ( !ShaderUtil()->OnSetFlexMesh( this, pMesh, nVertexOffsetInBytes ) )
		return;

	LOCK_SHADERAPI();
	m_nFlexVertOffsetInBytes = nVertexOffsetInBytes;	// Offset into dynamic mesh (in bytes)

	if ( pMesh )
	{		
		m_flexVertCount = pMesh->VertexCount();
		pMesh->MarkAsDrawn();

		CBaseMeshDX8 *pBaseMesh = static_cast<CBaseMeshDX8 *>(pMesh);
		m_pFlexVertexBuffer = pBaseMesh->GetVertexBuffer();

		m_bHasFlexVerts = true;
	}
	else
	{
		m_flexVertCount = 0;
		m_pFlexVertexBuffer = NULL;
		m_bHasFlexVerts = false;
	}
}

void CMeshDX8::DisableFlexMesh( )
{
	CMeshDX8::SetFlexMesh( NULL, 0 );
}

bool CMeshDX8::HasFlexMesh( ) const
{
	LOCK_SHADERAPI();
	return m_bHasFlexVerts;
}

void CMeshDX8::SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes )
{
	if ( !ShaderUtil()->OnSetColorMesh( this, pColorMesh, nVertexOffsetInBytes ) )
		return;
		
	LOCK_SHADERAPI();
	m_pColorMesh = ( CMeshDX8 * )pColorMesh; // dangerous conversion! garymcthack
	m_nColorMeshVertOffsetInBytes = nVertexOffsetInBytes;
	Assert( m_pColorMesh || ( nVertexOffsetInBytes == 0 ) );

#ifdef _DEBUG
	if ( pColorMesh )
	{
		int nVertexCount = VertexCount();
 		int numVertsColorMesh = m_pColorMesh->VertexCount();
		Assert( numVertsColorMesh >= nVertexCount );
	}
#endif
}


void CMeshDX8::HandleLateCreation( )
{
	if ( m_pVertexBuffer )
	{
		m_pVertexBuffer->HandleLateCreation();
	}
	if ( m_pIndexBuffer )
	{
		m_pIndexBuffer->HandleLateCreation();
	}
	if ( m_pFlexVertexBuffer )
	{
		m_pFlexVertexBuffer->HandleLateCreation();
	}

	if ( m_pColorMesh )
	{
		m_pColorMesh->HandleLateCreation();
	}
}


bool CMeshDX8::HasColorMesh( ) const
{
	LOCK_SHADERAPI();
	return (m_pColorMesh != NULL);
}


//-----------------------------------------------------------------------------
// Locks/ unlocks the vertex buffer
//-----------------------------------------------------------------------------
bool CMeshDX8::Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc )
{
	Assert( !m_IsVBLocked );

	// Just give the app crap buffers to fill up while we're suppressed...
	if ( g_pShaderDeviceDx8->IsDeactivated() || (nVertexCount == 0))
	{
		// Set up the vertex descriptor
		CVertexBufferBase::ComputeVertexDescription( 0, 0, desc );
		desc.m_nFirstVertex = 0;
		return false;
	}

	// Static vertex buffer case
	if (!m_pVertexBuffer)
	{
		int size = g_MeshMgr.VertexFormatSize( m_VertexFormat );
		m_pVertexBuffer = new CVertexBuffer( Dx9Device(), m_VertexFormat, 0, size, nVertexCount, m_pTextureGroupName, ShaderAPI()->UsingSoftwareVertexProcessing() );
	}

	// Lock it baby
	int nMaxVerts, nMaxIndices;
	g_MeshMgr.GetMaxToRender( this, false, &nMaxVerts, &nMaxIndices );
	if ( !g_pHardwareConfig->SupportsStreamOffset() )
	{
		// Without stream offset, we can't use VBs greater than 65535 verts (due to our using 16-bit indices)
		Assert( nVertexCount <= nMaxVerts );
	}

	unsigned char *pVertexMemory = m_pVertexBuffer->Lock( nVertexCount, desc.m_nFirstVertex );
	if ( !pVertexMemory )
	{
		if ( nVertexCount > nMaxVerts )
		{
			Assert( 0 );
			Error( "Too many verts for a dynamic vertex buffer (%d>%d) Tell a programmer to up VERTEX_BUFFER_SIZE.\n", 
				( int )nVertexCount, ( int )nMaxVerts );
		}
		else
		{
			// Check if paged pool is in critical state ( < 5% free )
			PAGED_POOL_INFO_t ppi;
			if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
				 ( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
			{
				Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" );
			}
			else
			{
				Assert( 0 );
				Error( "failed to lock vertex buffer in CMeshDX8::LockVertexBuffer: nVertexCount=%d, nFirstVertex=%d\n", nVertexCount, desc.m_nFirstVertex );
			}
		}
		CVertexBufferBase::ComputeVertexDescription( 0, 0, desc );
		return false;
	}

	// Set up the vertex descriptor
	CVertexBufferBase::ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc );
	m_IsVBLocked = true;

#ifdef RECORDING
	m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize;
	m_LockVertexBuffer = pVertexMemory;
#endif

	return true;
}

void CMeshDX8::Unlock( int nVertexCount, VertexDesc_t& desc )
{
	// NOTE: This can happen if another application finishes
	// initializing during the construction of a mesh
	if (!m_IsVBLocked)
		return;

	// This is recorded for debugging. . not sent to dx.
	RECORD_COMMAND( DX8_SET_VERTEX_BUFFER_FORMAT, 2 );
	RECORD_INT( m_pVertexBuffer->UID() );
	RECORD_INT( m_VertexFormat );
	
	RECORD_COMMAND( DX8_VERTEX_DATA, 3 );
	RECORD_INT( m_pVertexBuffer->UID() );
	RECORD_INT( m_LockVertexBufferSize );
	RECORD_STRUCT( m_LockVertexBuffer, m_LockVertexBufferSize );

	Assert(m_pVertexBuffer);
	m_pVertexBuffer->Unlock(nVertexCount);
	m_IsVBLocked = false;
}

//-----------------------------------------------------------------------------
// Locks/unlocks the index buffer
//-----------------------------------------------------------------------------
int CMeshDX8::Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &desc )
{
	Assert( !m_IsIBLocked );

	// Just give the app crap buffers to fill up while we're suppressed...
	if ( g_pShaderDeviceDx8->IsDeactivated() || (nIndexCount == 0))
	{
		// Set up a bogus index descriptor
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;
		return 0;
	}

	// Static vertex buffer case
	if (!m_pIndexBuffer)
	{
		SafeAssign( &m_pIndexBuffer, new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing() ) );
	}

	int startIndex;
	desc.m_pIndices = m_pIndexBuffer->Lock( bReadOnly, nIndexCount, startIndex, nFirstIndex );
	if( !desc.m_pIndices )
	{
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;

		// Check if paged pool is in critical state ( < 5% free )
		PAGED_POOL_INFO_t ppi;
		if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
			( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
		{
			Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" );
		}
		else
		{
			Assert( 0 );
			Error( "failed to lock index buffer in CMeshDX8::LockIndexBuffer\n" );
		}

		return 0;
	}
	
	desc.m_nIndexSize = 1;
	m_IsIBLocked = true;

#if defined( RECORDING ) || defined( CHECK_INDICES )
	m_LockIndexBufferSize = nIndexCount * 2;
	m_LockIndexBuffer = desc.m_pIndices;
#endif

	return startIndex;
}


void CMeshDX8::Unlock( int nIndexCount, IndexDesc_t &desc )
{
	// NOTE: This can happen if another application finishes
	// initializing during the construction of a mesh
	if (!m_IsIBLocked)
		return;

	RECORD_COMMAND( DX8_INDEX_DATA, 3 );
	RECORD_INT( m_pIndexBuffer->UID() );
	RECORD_INT( m_LockIndexBufferSize );
	RECORD_STRUCT( m_LockIndexBuffer, m_LockIndexBufferSize );

	Assert(m_pIndexBuffer);

#ifdef CHECK_INDICES
	m_pIndexBuffer->UpdateShadowIndices( ( unsigned short * )m_LockIndexBuffer );
#endif // CHECK_INDICES
	
	// Unlock, and indicate how many vertices we actually used
	m_pIndexBuffer->Unlock(nIndexCount);
	m_IsIBLocked = false;
}


//-----------------------------------------------------------------------------
// Locks/unlocks the entire mesh
//-----------------------------------------------------------------------------
void CMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	ShaderUtil()->SyncMatrices();

	g_ShaderMutex.Lock();
	VPROF( "CMeshDX8::LockMesh" );		
	Lock( nVertexCount, false, *static_cast<VertexDesc_t*>( &desc ) );
	if ( m_Type != MATERIAL_POINTS )
	{
		Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) );
	}
	else
	{
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;
	}

	CBaseMeshDX8::m_bMeshLocked = true;
}


void CMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	VPROF( "CMeshDX8::UnlockMesh" );

	Assert( CBaseMeshDX8::m_bMeshLocked );

	Unlock( nVertexCount, *static_cast<VertexDesc_t*>( &desc ) );
	if ( m_Type != MATERIAL_POINTS )
	{
		Unlock( nIndexCount, *static_cast<IndexDesc_t*>( &desc ) );
	}
																	    
	// The actual # we wrote
	m_NumVertices = nVertexCount;
	m_NumIndices = nIndexCount;

	CBaseMeshDX8::m_bMeshLocked = false;
	g_ShaderMutex.Unlock();
}

 
//-----------------------------------------------------------------------------
// Locks mesh for modifying
//-----------------------------------------------------------------------------
void CMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
{
	VPROF( "CMeshDX8::ModifyBegin" );

	// Just give the app crap buffers to fill up while we're suppressed...
	if ( g_pShaderDeviceDx8->IsDeactivated())
	{
		// Set up a bogus descriptor
		g_MeshMgr.ComputeVertexDescription( 0, 0, desc );
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;
		return;
	}

	Assert( m_pVertexBuffer );

	// Lock it baby
	unsigned char* pVertexMemory = m_pVertexBuffer->Modify( bReadOnly, nFirstVertex, nVertexCount );
	if ( pVertexMemory )
	{
		m_IsVBLocked = true;
		g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc );

#ifdef RECORDING
		m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize;
		m_LockVertexBuffer = pVertexMemory;
#endif
	}

	desc.m_nFirstVertex = nFirstVertex;

	Lock( bReadOnly, nFirstIndex, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) );
}

void CMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
{
	ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc );
}

void CMeshDX8::ModifyEnd( MeshDesc_t& desc )
{
	VPROF( "CMeshDX8::ModifyEnd" );
	Unlock( 0, *static_cast<IndexDesc_t*>( &desc ) );
	Unlock( 0, *static_cast<VertexDesc_t*>( &desc ) );
}


//-----------------------------------------------------------------------------
// returns the # of vertices (static meshes only)
//-----------------------------------------------------------------------------
int CMeshDX8::VertexCount() const
{
	return m_pVertexBuffer ? m_pVertexBuffer->VertexCount() : 0;
}


//-----------------------------------------------------------------------------
// returns the # of indices 
//-----------------------------------------------------------------------------
int CMeshDX8::IndexCount( ) const
{
	return m_pIndexBuffer ? m_pIndexBuffer->IndexCount() : 0;
}


//-----------------------------------------------------------------------------
// Sets up the vertex and index buffers
//-----------------------------------------------------------------------------
void CMeshDX8::UseIndexBuffer( CIndexBuffer* pBuffer )
{
	SafeAssign( &m_pIndexBuffer, pBuffer );
}

void CMeshDX8::UseVertexBuffer( CVertexBuffer* pBuffer )
{
	m_pVertexBuffer = pBuffer;
}


//-----------------------------------------------------------------------------
// Sets the primitive type
//-----------------------------------------------------------------------------
void CMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type )
{
	Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) );
	if ( !ShaderUtil()->OnSetPrimitiveType( this, type ) )
	{
		return;
	}

	LOCK_SHADERAPI();
	m_Type = type;
	m_Mode = ComputeMode( type );
}

MaterialPrimitiveType_t CMeshDX8::GetPrimitiveType( ) const
{
	return m_Type;
}


//-----------------------------------------------------------------------------
// Computes the number of primitives we're gonna draw
//-----------------------------------------------------------------------------
int CMeshDX8::NumPrimitives( int nVertexCount, int nIndexCount ) const
{
	switch(m_Mode)
	{
	case D3DPT_POINTLIST:
		return nVertexCount;
		
	case D3DPT_LINELIST:
		return nIndexCount / 2;

	case D3DPT_TRIANGLELIST:
		return nIndexCount / 3;

	case D3DPT_TRIANGLESTRIP:
		return nIndexCount - 2;

	default:
		// invalid, baby!
		Assert(0);
	}

	return 0;
}


//-----------------------------------------------------------------------------
// Checks if it's a valid format
//-----------------------------------------------------------------------------
#ifdef _DEBUG
static void OutputVertexFormat( VertexFormat_t format )
{
	// FIXME: this is a duplicate of the function in meshdx8.cpp
	VertexCompressionType_t compressionType = CompressionType( format );

	if ( format & VERTEX_POSITION )
	{
		Warning( "VERTEX_POSITION|" );
	}
	if ( format & VERTEX_NORMAL )
	{
		if ( compressionType == VERTEX_COMPRESSION_ON )
			Warning( "VERTEX_NORMAL[COMPRESSED]|" );
		else
			Warning( "VERTEX_NORMAL|" );
	}
	if ( format & VERTEX_COLOR )
	{
		Warning( "VERTEX_COLOR|" );
	}
	if ( format & VERTEX_SPECULAR )
	{
		Warning( "VERTEX_SPECULAR|" );
	}
	if ( format & VERTEX_TANGENT_S )
	{
		Warning( "VERTEX_TANGENT_S|" );
	}
	if ( format & VERTEX_TANGENT_T )
	{
		Warning( "VERTEX_TANGENT_T|" );
	}
	if ( format & VERTEX_BONE_INDEX )
	{
		Warning( "VERTEX_BONE_INDEX|" );
	}
	if ( format & VERTEX_FORMAT_VERTEX_SHADER )
	{
		Warning( "VERTEX_FORMAT_VERTEX_SHADER|" );
	}
	Warning( "\nBone weights: %d (%s)\n", NumBoneWeights( format ),
		( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) );
	Warning( "user data size: %d (%s)\n", UserDataSize( format ),
		( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) );
	Warning( "num tex coords: %d\n", NumTextureCoordinates( format ) );
	// NOTE: This doesn't print texcoord sizes.
}
#endif

bool CMeshDX8::IsValidVertexFormat( VertexFormat_t vertexFormat )
{
	// FIXME: Make this a debug-only check on say 6th July 2007 (after a week or so's testing)
	//        (i.e. avoid the 360 release build perf. hit for when we ship)
	bool bCheckCompression = ( m_VertexFormat & VERTEX_FORMAT_COMPRESSED ) &&
							( ( vertexFormat == VERTEX_FORMAT_INVALID ) || ( ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) );

	if ( bCheckCompression || IsPC() || IsDebug() )
	{
		IMaterialInternal* pMaterial = ShaderAPI()->GetBoundMaterial();
		Assert( pMaterial );

		// the material format should match the vertex usage, unless another format is passed in
		if ( vertexFormat == VERTEX_FORMAT_INVALID )
		{
			vertexFormat = pMaterial->GetVertexUsage() & ~( VERTEX_FORMAT_VERTEX_SHADER | VERTEX_FORMAT_USE_EXACT_FORMAT );

			// Blat out unused fields
			vertexFormat &= ~g_MeshMgr.UnusedVertexFields();
			int nUnusedTextureCoords = g_MeshMgr.UnusedTextureCoords();
			for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i )
			{
				if ( nUnusedTextureCoords & ( 1 << i ) )
				{
					vertexFormat &= ~VERTEX_TEXCOORD_MASK( i );
				}
			}
		}
		else
		{
			vertexFormat &= ~( VERTEX_FORMAT_VERTEX_SHADER | VERTEX_FORMAT_USE_EXACT_FORMAT );
		}

		bool bIsValid = (( VERTEX_FORMAT_FIELD_MASK & vertexFormat ) & ( VERTEX_FORMAT_FIELD_MASK & ~m_VertexFormat )) == 0;

		if ( m_VertexFormat & VERTEX_FORMAT_COMPRESSED )
		{
			// We shouldn't get compressed verts if this material doesn't support them!
			if ( ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) == 0 )
			{
				static int numWarnings = 0;
				if ( numWarnings++ == 0 )
				{
					// NOTE: ComputeVertexFormat() will make sure no materials support VERTEX_FORMAT_COMPRESSED
					//       if vertex compression is disabled in the config
					if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE )
						Warning( "ERROR: Compressed vertices in use but vertex compression is disabled (or not supported on this hardware)!\n" );
					else
						Warning( "ERROR: Compressed vertices in use but material does not support them!\n" );
				}
				Assert( 0 );
				bIsValid = false;
			}
		}

		bIsValid = bIsValid && UserDataSize( m_VertexFormat ) >= UserDataSize( vertexFormat );

		for ( int i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ )
		{
			if ( TexCoordSize( i, m_VertexFormat ) < TexCoordSize( i, vertexFormat ) )
			{
				bIsValid = false;
			}
		}
		
		// NOTE: It can totally be valid to have more weights than the current number of bones.
		// The -1 here is because if we have N bones, we can have only (N-1) weights,
		// since the Nth is implied (the weights sum to 1).
		int nWeightCount = NumBoneWeights( m_VertexFormat );
		bIsValid = bIsValid && ( nWeightCount >= ( g_pShaderAPI->GetCurrentNumBones() - 1 ) );

#ifdef _DEBUG
		if ( !bIsValid )
		{
			Warning( "Material Format:" );
			if ( g_pShaderAPI->GetCurrentNumBones() > 0 )
			{
				vertexFormat |= VERTEX_BONE_INDEX;
				vertexFormat &= ~VERTEX_BONE_WEIGHT_MASK;
				vertexFormat |= VERTEX_BONEWEIGHT( 2 );
			}

			OutputVertexFormat( vertexFormat );
			Warning( "Mesh Format:" );
			OutputVertexFormat( m_VertexFormat );
		}
#endif
		return bIsValid;
	}

	return true;
}


//-----------------------------------------------------------------------------
// Stream source setting methods
//-----------------------------------------------------------------------------
void CMeshDX8::SetVertexIDStreamState()
{
	// FIXME: this method duplicates the code in CMeshMgr::SetVertexIDStreamState

	if ( IsX360() )
		return;

	bool bUsingVertexID = IsUsingVertexID();
	if ( bUsingVertexID != g_bUsingVertexID )
	{
		if ( bUsingVertexID )
		{
			// NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH
			// It's because the indices (which are not 0 based for dynamic buffers)
			// are accessing both the vertexID buffer + the regular vertex buffer.
			// This *might* be fixable with baseVertexIndex?

			// NOTE: At the moment, vertex id is only used for hw morphing. I've got it
			// set up so that a shader that supports hw morphing always says it uses vertex id.
			// If we ever use vertex id for something other than hw morphing, we're going
			// to have to revisit how those shaders say they want to use vertex id
			// or  fix this some other way
			Assert( !g_pShaderAPI->IsHWMorphingEnabled() || !m_pVertexBuffer->IsDynamic() );

			CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( );
			RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
			RECORD_INT( pVertexIDBuffer->UID() );
			RECORD_INT( 3 );
			RECORD_INT( 0 );
			RECORD_INT( pVertexIDBuffer->VertexSize() );

			D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() );
			pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
		}
		else
		{
			RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
			RECORD_INT( -1 );	// vertex buffer id
			RECORD_INT( 3 );	// stream
			RECORD_INT( 0 );	// vertex offset
			RECORD_INT( 0 );	// vertex size

			D3DSetStreamSource( 3, 0, 0, 0 );
		}
		g_bUsingVertexID = bUsingVertexID;
	}
}

void CMeshDX8::SetColorStreamState()
{
	if ( ( m_pColorMesh != g_pLastColorMesh ) || ( m_nColorMeshVertOffsetInBytes != g_nLastColorMeshVertOffsetInBytes ) )
	{
		if ( m_pColorMesh )
		{
			RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
			RECORD_INT( m_pColorMesh->GetVertexBuffer()->UID() );
			RECORD_INT( 1 );
			RECORD_INT( m_nColorMeshVertOffsetInBytes );
			RECORD_INT( m_pColorMesh->GetVertexBuffer()->VertexSize() );

			D3DSetStreamSource( 1, m_pColorMesh->GetVertexBuffer()->GetInterface(), 
								m_nColorMeshVertOffsetInBytes, m_pColorMesh->GetVertexBuffer()->VertexSize() );
			m_pColorMesh->GetVertexBuffer()->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
		}
		else
		{
			RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
			RECORD_INT( -1 );	// vertex buffer id
			RECORD_INT( 1 );	// stream
			RECORD_INT( 0 );	// vertex offset
			RECORD_INT( 0 );	// vertex size

			D3DSetStreamSource( 1, 0, 0, 0 );
		}
		g_pLastColorMesh = m_pColorMesh;
		g_nLastColorMeshVertOffsetInBytes = m_nColorMeshVertOffsetInBytes;
	}
}

void CMeshDX8::SetVertexStreamState( int nVertOffsetInBytes )
{
	// Calls in here assume shader support...
	if ( HardwareConfig()->SupportsVertexAndPixelShaders() )
	{
		if ( HasFlexMesh() )
		{
			// m_pFlexVertexBuffer is the flex buffer down inside the CMeshMgr singleton
			D3DSetStreamSource( 2, m_pFlexVertexBuffer->GetInterface(), m_nFlexVertOffsetInBytes, m_pFlexVertexBuffer->VertexSize() );

			if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 )
			{
				float c[4] = { 1.0f, g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ? 1.0f : 0.0f, 0.0f, 0.0f };
				ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
			}

			g_bFlexMeshStreamSet = true;
		}
		else
		{
			Assert( nVertOffsetInBytes == 0 );
			Assert( m_pVertexBuffer );

			// HACK...point stream 2 at the same VB which is bound to stream 0...
			// NOTE: D3D debug DLLs will RIP if stream 0 has a smaller stride than the largest
			//       offset in the stream 2 vertex decl elements (which are position(12)+wrinkle(4)+normal(12))
			// If this fires, go find the material/shader which is requesting a really 'thin'
			// stream 0 vertex, and fatten it up slightly (e.g. add a D3DCOLOR element)
			int minimumStreamZeroStride = 4 * sizeof( float );
			Assert( m_pVertexBuffer->VertexSize() >= minimumStreamZeroStride );
			if ( m_pVertexBuffer->VertexSize() < minimumStreamZeroStride )
			{
				static bool bWarned = false;
				if( !bWarned )
				{
					Warning( "Shader specifying too-thin vertex format, should be at least %d bytes! (Suppressing furthur warnings)\n", minimumStreamZeroStride );
					bWarned = true;
				}
			}

			// Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 1 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia)
			// togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency.
			D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 );

			if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 )
			{
				float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
				ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
			}

			g_bFlexMeshStreamSet = false;
		}
	}

	// MESHFIXME: Make sure this jives between the mesh/ib/vb version.
#ifdef _X360
	if ( ( g_pLastVertex != m_pVertexBuffer ) || ( m_pVertexBuffer->IsDynamic() ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) )
#else
	if ( ( g_pLastVertex != m_pVertexBuffer ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) )
#endif
	{
		Assert( m_pVertexBuffer );

		RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
		RECORD_INT( m_pVertexBuffer->UID() );
		RECORD_INT( 0 );
		RECORD_INT( nVertOffsetInBytes );
		RECORD_INT( m_pVertexBuffer->VertexSize() );

		D3DSetStreamSource( 0, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() );
		m_pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
				
		g_pLastVertex = m_pVertexBuffer;
		g_nLastVertOffsetInBytes = nVertOffsetInBytes;
	}
}

void CMeshDX8::SetIndexStreamState( int firstVertexIdx )
{
#ifdef _X360
	if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( m_pIndexBuffer->IsDynamic() ) || ( firstVertexIdx != g_LastVertexIdx ) )
#else
	if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( firstVertexIdx != g_LastVertexIdx ) )
#endif
	{
		Assert( m_pIndexBuffer );

		RECORD_COMMAND( DX8_SET_INDICES, 2 );
		RECORD_INT( m_pIndexBuffer->UID() );
		RECORD_INT( firstVertexIdx );

		Dx9Device()->SetIndices( m_pIndexBuffer->GetInterface() );
		m_pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
		m_FirstIndex = firstVertexIdx;

		SafeAssign( &g_pLastIndex, m_pIndexBuffer );
		g_pLastIndexBuffer = NULL;
		g_LastVertexIdx = firstVertexIdx;
	}
}

bool CMeshDX8::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat )
{
	// Can't set the state if we're deactivated
	if ( g_pShaderDeviceDx8->IsDeactivated() )
	{
		ResetMeshRenderState();
		return false;
	}

	g_LastVertexFormat = vertexFormat;

	SetVertexIDStreamState();
	SetColorStreamState();
	SetVertexStreamState( nVertexOffsetInBytes );
	SetIndexStreamState( nFirstVertexIdx );

	return true;
}


//-----------------------------------------------------------------------------
// Draws the static mesh
//-----------------------------------------------------------------------------
void CMeshDX8::Draw( int nFirstIndex, int nIndexCount )
{
	Assert( m_pVertexBuffer );
	if ( !m_pVertexBuffer )
	{
		return;
	}

	if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
	{
		MarkAsDrawn();
		return;
	}

	CPrimList primList;
	if( nFirstIndex == -1 || nIndexCount == 0 )
	{
		primList.m_FirstIndex = 0;
		primList.m_NumIndices = m_NumIndices;
	}
	else
	{
		primList.m_FirstIndex = nFirstIndex;
		primList.m_NumIndices = nIndexCount;
	}
	DrawInternal( &primList, 1 );
}

void CMeshDX8::Draw( CPrimList *pLists, int nLists )
{
	Assert( m_pVertexBuffer );
	if ( !m_pVertexBuffer )
	{
		return;
	}

	if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) )
	{
		MarkAsDrawn();
		return;
	}

	DrawInternal( pLists, nLists );
}


void CMeshDX8::DrawInternal( CPrimList *pLists, int nLists )
{
	HandleLateCreation();

	// Make sure there's something to draw..
	int i;
	for ( i=0; i < nLists; i++ )
	{
		if ( pLists[i].m_NumIndices > 0 )
			break;
	}
	if ( i == nLists )
		return;

	// can't do these in selection mode!
	Assert( !ShaderAPI()->IsInSelectionMode() );

	if ( !SetRenderState( 0, 0 ) )
		return;

	s_pPrims = pLists;
	s_nPrims = nLists;

#ifdef _DEBUG
	for ( i = 0; i < nLists; ++i)
	{
		Assert( pLists[i].m_NumIndices > 0 );
	}
#endif

	s_FirstVertex = 0;
	s_NumVertices = m_pVertexBuffer->VertexCount();

	DrawMesh();
}


#ifdef CHECK_INDICES
void CMeshDX8::CheckIndices( CPrimList *pPrim, int numPrimitives )
{
	// g_pLastVertex - this is the current vertex buffer
	// g_pLastColorMesh - this is the current color mesh, if there is one.
	// g_pLastIndex - this is the current index buffer.
	// vertoffset : m_FirstIndex
	if( m_Mode == D3DPT_TRIANGLELIST || m_Mode == D3DPT_TRIANGLESTRIP )
	{
		Assert( pPrim->m_FirstIndex >= 0 && pPrim->m_FirstIndex < g_pLastIndex->IndexCount() );
		int i;
		for( i = 0; i < 2; i++ )
		{
			CVertexBuffer *pMesh;
			if( i == 0 )
			{
				pMesh = g_pLastVertex;
				Assert( pMesh );
			}
			else
			{
				if( !g_pLastColorMesh )
				{
					continue;
				}
				pMesh = g_pLastColorMesh->m_pVertexBuffer;
				if( !pMesh )
				{
					continue;
				}
			}
			Assert( s_FirstVertex >= 0 && 
				(int)( s_FirstVertex + m_FirstIndex ) < pMesh->VertexCount() );
			int nIndexCount = 0;
			if( m_Mode == D3DPT_TRIANGLELIST )
			{
				nIndexCount = numPrimitives * 3;
			}
			else if( m_Mode == D3DPT_TRIANGLESTRIP )
			{
				nIndexCount = numPrimitives + 2;
			}
			else
			{
				Assert( 0 );
			}
			int j;
			for( j = 0; j < nIndexCount; j++ )
			{
				int index = g_pLastIndex->GetShadowIndex( j + pPrim->m_FirstIndex );
				if ( ( index < (int)s_FirstVertex ) || ( index >= (int)( s_FirstVertex + s_NumVertices ) ) )
					Warning("%s invalid index: %d [%u..%u]\n", __FUNCTION__, index, s_FirstVertex, s_FirstVertex + s_NumVertices - 1 );
				Assert( index >= (int)s_FirstVertex );
				Assert( index < (int)(s_FirstVertex + s_NumVertices) );
			}
		}
	}
}
#endif // CHECK_INDICES


//-----------------------------------------------------------------------------
// Actually does the dirty deed of rendering
//-----------------------------------------------------------------------------
void CMeshDX8::RenderPass()
{
	LOCK_SHADERAPI();
	VPROF( "CMeshDX8::RenderPass" );

	HandleLateCreation();

	Assert( m_Type != MATERIAL_HETEROGENOUS );

	// make sure the vertex format is a superset of the current material's
	// vertex format...
	if ( !IsValidVertexFormat( g_LastVertexFormat ) )
	{
		Warning( "Material %s does not support vertex format used by the mesh (maybe missing fields or mismatched vertex compression?), mesh will not be rendered. Grab a programmer!\n",
			ShaderAPI()->GetBoundMaterial()->GetName() );
		return;
	}

	for ( int iPrim=0; iPrim < s_nPrims; iPrim++ )
	{
		CPrimList *pPrim = &s_pPrims[iPrim];

		if ( pPrim->m_NumIndices == 0 )
			continue;

		if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) )
		{
			tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "Dx9Device_DrawPrimitive" );

			// (For point/instanced-quad lists, we don't actually fill in indices, but we treat it as
			// though there are indices for the list up until here).
			Dx9Device()->DrawPrimitive( m_Mode, s_FirstVertex, pPrim->m_NumIndices );
		}
		else
		{
			int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices );

#ifdef CHECK_INDICES
			CheckIndices( pPrim, numPrimitives );
#endif // CHECK_INDICES
			{
				VPROF( "Dx9Device()->DrawIndexedPrimitive" );
				VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
				VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives );
				VPROF_INCREMENT_GROUP_COUNTER( "render/DrawIndexedPrimitive", COUNTER_GROUP_TELEMETRY, 1 );
				VPROF_INCREMENT_GROUP_COUNTER( "render/numPrimitives", COUNTER_GROUP_TELEMETRY, 1 );

				Dx9Device()->DrawIndexedPrimitive( 
					m_Mode,			// Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method.

					m_FirstIndex,	// Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer.

					s_FirstVertex,	// Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex.
									// The first Vertex in the vertexbuffer that we are currently using for the current batch.

					s_NumVertices,	// Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex.

					pPrim->m_FirstIndex, // Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer.

					numPrimitives );// Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type.
			}
		}
	}

	if ( g_pLastVertex )
	{
		g_pLastVertex->MarkUsedInRendering();
	}

	if( g_pLastIndex )
	{
		g_pLastIndex->MarkUsedInRendering();
	}
}

//-----------------------------------------------------------------------------
//
// Dynamic mesh implementation
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CDynamicMeshDX8::CDynamicMeshDX8() : CMeshDX8( "CDynamicMeshDX8" )
{
	m_nBufferId = 0;
	ResetVertexAndIndexCounts();
}

CDynamicMeshDX8::~CDynamicMeshDX8()
{
}


//-----------------------------------------------------------------------------
// Initializes the dynamic mesh
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::Init( int nBufferId )
{
	m_nBufferId = nBufferId;
}


//-----------------------------------------------------------------------------
// Resets buffering state
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::ResetVertexAndIndexCounts()
{
	m_TotalVertices = m_TotalIndices = 0;
	m_FirstIndex = m_nFirstVertex = -1;
	m_HasDrawn = false;
}


//-----------------------------------------------------------------------------
// Resets the state in case of a task switch
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::Reset()
{
	m_VertexFormat = 0;
	m_pVertexBuffer = 0;
	SafeRelease( &m_pIndexBuffer );
	ResetVertexAndIndexCounts();

	// Force the render state to be updated next time
	ResetMeshRenderState();
}


//-----------------------------------------------------------------------------
// Sets the vertex format associated with the dynamic mesh
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::SetVertexFormat( VertexFormat_t format )
{
	if ( g_pShaderDeviceDx8->IsDeactivated())
		return;

	if ( CompressionType( format ) != VERTEX_COMPRESSION_NONE )
	{
		// UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: CMeshBuilder gets slower)
		Warning( "ERROR: dynamic meshes cannot use compressed vertices!\n" );
		Assert( 0 );
		format &= ~VERTEX_FORMAT_COMPRESSED;
	}

	if ((format != m_VertexFormat) || m_VertexOverride || m_IndexOverride)
	{
		m_VertexFormat = format;
		UseVertexBuffer( g_MeshMgr.FindOrCreateVertexBuffer( m_nBufferId, format ) );

		if ( m_nBufferId == 0 )
		{
			UseIndexBuffer( g_MeshMgr.GetDynamicIndexBuffer() );
		}

		m_VertexOverride = m_IndexOverride = false;
	}
}

void CDynamicMeshDX8::OverrideVertexBuffer( CVertexBuffer* pVertexBuffer )
{
	UseVertexBuffer( pVertexBuffer );
	m_VertexOverride = true;
}

void CDynamicMeshDX8::OverrideIndexBuffer( CIndexBuffer* pIndexBuffer )
{
	UseIndexBuffer( pIndexBuffer );
	m_IndexOverride = true;
}


//-----------------------------------------------------------------------------
// Do I need to reset the vertex format?
//-----------------------------------------------------------------------------
bool CDynamicMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const
{
	return m_VertexOverride || m_IndexOverride || (m_VertexFormat != fmt);
}



//-----------------------------------------------------------------------------
// Locks/unlocks the entire mesh
//-----------------------------------------------------------------------------
bool CDynamicMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const
{
	if ( g_pShaderDeviceDx8->IsDeactivated() )
		return false;

	Assert( m_pVertexBuffer != NULL );

	// We need space in both the vertex and index buffer
	return m_pVertexBuffer->HasEnoughRoom( nVertexCount ) && 
		m_pIndexBuffer->HasEnoughRoom( nIndexCount );
}


//-----------------------------------------------------------------------------
// returns the number of indices in the mesh
//-----------------------------------------------------------------------------
int CDynamicMeshDX8::IndexCount( ) const
{
	return m_TotalIndices;
}


//-----------------------------------------------------------------------------
// Operation to do pre-lock	(only called for buffered meshes)
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::PreLock()
{
	if (m_HasDrawn)
	{
		// Start again then
		ResetVertexAndIndexCounts();
	}
}


//-----------------------------------------------------------------------------
// Locks/unlocks the entire mesh
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s %d %d", __FUNCTION__, nVertexCount, nIndexCount );

	ShaderUtil()->SyncMatrices();

	{
		tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "g_ShaderMutex.Lock" );
		g_ShaderMutex.Lock();
	}

	// Yes, this may well also be called from BufferedMesh but that's ok
	{
		tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "PreLock" );
		PreLock();
	}

	if (m_VertexOverride)
	{
		nVertexCount = 0;
	}

	if (m_IndexOverride)
	{
		nIndexCount = 0;
	}

	{
		tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "Lock" );
		Lock( nVertexCount, false, *static_cast<VertexDesc_t*>( &desc ) );
	}

	if (m_nFirstVertex < 0)
	{
		m_nFirstVertex = desc.m_nFirstVertex;
	}

	// When we're using a static index buffer or a flex mesh, the indices assume vertices start at 0
	if ( m_IndexOverride || HasFlexMesh() )
	{
		desc.m_nFirstVertex -= m_nFirstVertex;
	}

	// Don't add indices for points; DrawIndexedPrimitive not supported for them.
	if ( m_Type != MATERIAL_POINTS )
	{
		int nFirstIndex;
		{
			tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "Lock nFirstIndex" );
			nFirstIndex = Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) );
		}
		if (m_FirstIndex < 0)
		{
			m_FirstIndex = nFirstIndex;
		}
	}
	else
	{
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;
	}

	CBaseMeshDX8::m_bMeshLocked = true;
}


//-----------------------------------------------------------------------------
// Unlocks the mesh
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	m_TotalVertices += nVertexCount;
	m_TotalIndices += nIndexCount;

	if (DebugTrace())
	{
		Spew( nVertexCount, nIndexCount, desc );
	}

	CMeshDX8::UnlockMesh( nVertexCount, nIndexCount, desc );

	// This is handled in the CMeshDX8::UnlockMesh above.
	//CBaseMeshDX8::m_bMeshLocked = false;
}


//-----------------------------------------------------------------------------
// Draws it
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::Draw( int nFirstIndex, int nIndexCount )
{
	if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
	{
		MarkAsDrawn();
		return;
	}

	VPROF( "CDynamicMeshDX8::Draw" );

	m_HasDrawn = true;

	if (m_IndexOverride || m_VertexOverride || 
		( ( m_TotalVertices > 0 ) && ( m_TotalIndices > 0 || m_Type == MATERIAL_POINTS || m_Type == MATERIAL_INSTANCED_QUADS ) ) )
	{
		Assert( !m_IsDrawing );
		
		HandleLateCreation( );

		// only have a non-zero first vertex when we are using static indices
		int nFirstVertex = m_VertexOverride ? 0 : m_nFirstVertex;
		int actualFirstVertex = m_IndexOverride ? nFirstVertex : 0;
		int nVertexOffsetInBytes = HasFlexMesh() ? nFirstVertex * g_MeshMgr.VertexFormatSize( GetVertexFormat() ) : 0;
		int baseIndex = m_IndexOverride ? 0 : m_FirstIndex;

		// Overriding with the dynamic index buffer, preserve state!
		if ( m_IndexOverride && m_pIndexBuffer == g_MeshMgr.GetDynamicIndexBuffer() )
		{
			baseIndex = m_FirstIndex;
		}

		VertexFormat_t fmt = m_VertexOverride ? GetVertexFormat() : VERTEX_FORMAT_INVALID;
		if ( !SetRenderState( nVertexOffsetInBytes, actualFirstVertex, fmt ) )
			return;

		// Draws a portion of the mesh
		int numVertices = m_VertexOverride ? m_pVertexBuffer->VertexCount() : m_TotalVertices;
		if ((nFirstIndex != -1) && (nIndexCount != 0))
		{
			nFirstIndex += baseIndex;
		}
		else
		{
			// by default we draw the whole thing
			nFirstIndex = baseIndex;
			if( m_IndexOverride )
			{
				nIndexCount = m_pIndexBuffer->IndexCount();
				Assert( nIndexCount != 0 );
			}
			else
			{
				nIndexCount = m_TotalIndices;
				// Fake out the index count	if we're drawing points/instanced-quads
				if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) )
				{
					nIndexCount = m_TotalVertices;
				}
				Assert( nIndexCount != 0 );
			}
		}

		// Fix up nFirstVertex to indicate the first vertex used in the data
		if ( !HasFlexMesh() )
		{
			actualFirstVertex = nFirstVertex - actualFirstVertex;
		}
		
		s_FirstVertex = actualFirstVertex;
		s_NumVertices = numVertices;
		
		// Build a primlist with 1 element..
		CPrimList prim;
		prim.m_FirstIndex = nFirstIndex;
		prim.m_NumIndices = nIndexCount;
		Assert( nIndexCount != 0 );
		s_pPrims = &prim;
		s_nPrims = 1;

		DrawMesh();

		s_pPrims = NULL;
	}
}


//-----------------------------------------------------------------------------
// This is useful when we need to dynamically modify data; just set the
// render state and draw the pass immediately
//-----------------------------------------------------------------------------
void CDynamicMeshDX8::DrawSinglePassImmediately()
{
	if ((m_TotalVertices > 0) || (m_TotalIndices > 0))
	{
		Assert( !m_IsDrawing );

		// Set the render state
		if ( SetRenderState( 0, 0 ) )
		{
			s_FirstVertex = m_nFirstVertex;
			s_NumVertices = m_TotalVertices;

			// Make a temporary PrimList to hold the indices.
			CPrimList prim( m_FirstIndex, m_TotalIndices );
			Assert( m_TotalIndices != 0 );
			s_pPrims = &prim;
			s_nPrims = 1;

			// Render it
			RenderPass();
		}

		// We're done with our data
		ResetVertexAndIndexCounts();
	}
}

//-----------------------------------------------------------------------------
//
// A mesh that stores temporary vertex data in the correct format (for modification)
//
//-----------------------------------------------------------------------------
// Used in rendering sub-parts of the mesh
unsigned int CTempMeshDX8::s_NumIndices;
unsigned int CTempMeshDX8::s_FirstIndex;

//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CTempMeshDX8::CTempMeshDX8( bool isDynamic ) : m_VertexSize(0xFFFF), m_IsDynamic(isDynamic)
{
#ifdef DBGFLAG_ASSERT
	m_Locked = false;
	m_InPass = false;
#endif
}

CTempMeshDX8::~CTempMeshDX8()
{
}

//-----------------------------------------------------------------------------
// Is the temp mesh dynamic?
//-----------------------------------------------------------------------------
bool CTempMeshDX8::IsDynamic() const
{
	return m_IsDynamic;
}


//-----------------------------------------------------------------------------
// Sets the vertex format
//-----------------------------------------------------------------------------
void CTempMeshDX8::SetVertexFormat( VertexFormat_t format )
{
	CBaseMeshDX8::SetVertexFormat(format);
	m_VertexSize = g_MeshMgr.VertexFormatSize( format );
}

//-----------------------------------------------------------------------------
// returns the # of vertices (static meshes only)
//-----------------------------------------------------------------------------
int CTempMeshDX8::VertexCount() const
{
	return m_VertexSize ? m_VertexData.Count() / m_VertexSize : 0;
}

//-----------------------------------------------------------------------------
// returns the # of indices 
//-----------------------------------------------------------------------------
int CTempMeshDX8::IndexCount( ) const
{
	return m_IndexData.Count();
}

void CTempMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
{
	Assert( !m_Locked );

	m_LockedVerts = nVertexCount;
	m_LockedIndices = nIndexCount;

	if( nVertexCount > 0 )
	{
		int vertexByteOffset = m_VertexSize * nFirstVertex;
		
		// Lock it baby
		unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset];
		
		// Compute the vertex index..
		desc.m_nFirstVertex = vertexByteOffset / m_VertexSize;
		
		// Set up the mesh descriptor
		g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc );
	}
	else
	{
		desc.m_nFirstVertex = 0;
		// Set up the mesh descriptor
		g_MeshMgr.ComputeVertexDescription( 0, 0, desc );
	}

	if (m_Type != MATERIAL_POINTS && nIndexCount > 0 )
	{
		desc.m_pIndices = &m_IndexData[nFirstIndex];
		desc.m_nIndexSize = 1;
	}
	else
	{
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;
	}

#ifdef DBGFLAG_ASSERT
	m_Locked = true;
#endif
}

void CTempMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
{
	ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc );
}

void CTempMeshDX8::ModifyEnd( MeshDesc_t& desc )
{
#ifdef DBGFLAG_ASSERT
	Assert( m_Locked );
	m_Locked = false;
#endif
}

//-----------------------------------------------------------------------------
// Locks/unlocks the mesh
//-----------------------------------------------------------------------------
void CTempMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	ShaderUtil()->SyncMatrices();

	g_ShaderMutex.Lock();

	Assert( !m_Locked );

	m_LockedVerts = nVertexCount;
	m_LockedIndices = nIndexCount;

	if( nVertexCount > 0 )
	{
		int vertexByteOffset = m_VertexData.AddMultipleToTail( m_VertexSize * nVertexCount );
		
		// Lock it baby
		unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset];
		
		// Compute the vertex index..
		desc.m_nFirstVertex = vertexByteOffset / m_VertexSize;
		
		// Set up the mesh descriptor
		g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc );
	}
	else
	{
		desc.m_nFirstVertex = 0;
		// Set up the mesh descriptor
		g_MeshMgr.ComputeVertexDescription( 0, 0, desc );
	}

	if (m_Type != MATERIAL_POINTS && nIndexCount > 0 )
	{
		int nFirstIndex = m_IndexData.AddMultipleToTail( nIndexCount );
		desc.m_pIndices = &m_IndexData[nFirstIndex];
		desc.m_nIndexSize = 1;
	}
	else
	{
		desc.m_pIndices = g_nScratchIndexBuffer;
		desc.m_nIndexSize = 0;
	}

#ifdef DBGFLAG_ASSERT
	m_Locked = true;
#endif

	CBaseMeshDX8::m_bMeshLocked = true;
}

void CTempMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	Assert( m_Locked );

	// Remove unused vertices and indices
	int verticesToRemove = m_LockedVerts - nVertexCount;
	if( verticesToRemove != 0 )
	{
		m_VertexData.RemoveMultiple( m_VertexData.Count() - verticesToRemove, verticesToRemove );
	}

	int indicesToRemove = m_LockedIndices - nIndexCount;
	if( indicesToRemove != 0 )
	{
		m_IndexData.RemoveMultiple( m_IndexData.Count() - indicesToRemove, indicesToRemove );
	}

#ifdef DBGFLAG_ASSERT
	m_Locked = false;
#endif

	CBaseMeshDX8::m_bMeshLocked = false;

	g_ShaderMutex.Unlock();
}

//-----------------------------------------------------------------------------
// Sets the primitive type
//-----------------------------------------------------------------------------
void CTempMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type )
{
	// FIXME: Support MATERIAL_INSTANCED_QUADS for CTempMeshDX8 (X360 only)
	Assert( ( type != MATERIAL_INSTANCED_QUADS ) /* || IsX360() */ );
	m_Type = type;
}

MaterialPrimitiveType_t CTempMeshDX8::GetPrimitiveType( ) const
{
	return m_Type;
}

//-----------------------------------------------------------------------------
// Gets the dynamic mesh
//-----------------------------------------------------------------------------
CDynamicMeshDX8* CTempMeshDX8::GetDynamicMesh( )
{
	return static_cast<CDynamicMeshDX8*>(g_MeshMgr.GetActualDynamicMesh( m_VertexFormat ));
}

//-----------------------------------------------------------------------------
// Draws the entire mesh
//-----------------------------------------------------------------------------
void CTempMeshDX8::Draw( int nFirstIndex, int nIndexCount )
{
	if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
	{
		MarkAsDrawn();
		return;
	}

	if (m_VertexData.Count() > 0)
	{
		if ( !g_pShaderDeviceDx8->IsDeactivated() )
		{
#ifdef DRAW_SELECTION
			if (!g_bDrawSelection && !ShaderAPI()->IsInSelectionMode())
#else
			if (!ShaderAPI()->IsInSelectionMode())
#endif
			{
				s_FirstIndex = nFirstIndex;
				s_NumIndices = nIndexCount;

				DrawMesh( );

				// This assertion fails if a BeginPass() call was not matched by 
				// a RenderPass() call
				Assert(!m_InPass);
			}
			else
			{
				TestSelection();
			}
		}

		// Clear out the data if this temp mesh is a dynamic one...
		if (m_IsDynamic)
		{
			m_VertexData.RemoveAll();
			m_IndexData.RemoveAll();
		}
	}
}

void CTempMeshDX8::CopyToMeshBuilder( 
	int iStartVert,		// Which vertices to copy.
	int nVerts, 
	int iStartIndex,	// Which indices to copy.
	int nIndices, 
	int indexOffset,	// This is added to each index.
	CMeshBuilder &builder )
{
	int startOffset = iStartVert * m_VertexSize;
	int endOffset = (iStartVert + nVerts) * m_VertexSize;
	Assert( startOffset >= 0 && startOffset <= m_VertexData.Count() );
	Assert( endOffset >= 0 && endOffset <= m_VertexData.Count() && endOffset >= startOffset );
	if ( endOffset > startOffset )
	{
		// FIXME: make this a method of CMeshBuilder (so the 'Position' pointer accessor can be removed)
		//        make sure it takes a VertexFormat_t parameter for src/dest match validation
		memcpy( (void*)builder.Position(), &m_VertexData[startOffset], endOffset - startOffset );
		builder.AdvanceVertices( nVerts );
	}

	for ( int i = 0; i < nIndices; ++i )
	{
		builder.Index( m_IndexData[iStartIndex+i] + indexOffset );
		builder.AdvanceIndex();
	}		
}

//-----------------------------------------------------------------------------
// Selection mode helper functions
//-----------------------------------------------------------------------------
static void ComputeModelToView( D3DXMATRIX& modelToView )
{
	// Get the modelview matrix...
	D3DXMATRIX world, view;
	ShaderAPI()->GetMatrix( MATERIAL_MODEL, (float*)&world );
	ShaderAPI()->GetMatrix( MATERIAL_VIEW, (float*)&view );
	D3DXMatrixMultiply( &modelToView, &world, &view );
}

static float ComputeCullFactor( )
{
	D3DCULL cullMode = ShaderAPI()->GetCullMode();

	float cullFactor;
	switch(cullMode)
	{
	case D3DCULL_CCW:
		cullFactor = -1.0f;
		break;
		
	case D3DCULL_CW:
		cullFactor = 1.0f;
		break;

	default:
		cullFactor = 0.0f;
		break;
	};

	return cullFactor;
}

//-----------------------------------------------------------------------------
// Clip to viewport
//-----------------------------------------------------------------------------
static int g_NumClipVerts;
static D3DXVECTOR3 g_ClipVerts[16];

static bool PointInsidePlane( D3DXVECTOR3* pVert, int normalInd, float val, bool nearClip )
{
	if ((val > 0) || nearClip)
		return (val - (*pVert)[normalInd] >= 0);
	else
		return ((*pVert)[normalInd] - val >= 0);
}

static void IntersectPlane( D3DXVECTOR3* pStart, D3DXVECTOR3* pEnd, 
						    int normalInd, float val, D3DXVECTOR3* pOutVert )
{
	D3DXVECTOR3 dir;
	D3DXVec3Subtract( &dir, pEnd, pStart );
	Assert( dir[normalInd] != 0.0f );
	float t = (val - (*pStart)[normalInd]) / dir[normalInd];
	pOutVert->x = pStart->x + dir.x * t;
	pOutVert->y = pStart->y + dir.y * t;
	pOutVert->z = pStart->z + dir.z * t;

	// Avoid any precision problems.
	(*pOutVert)[normalInd] = val;
}

static int ClipTriangleAgainstPlane( D3DXVECTOR3** ppVert, int nVertexCount, 
			D3DXVECTOR3** ppOutVert, int normalInd, float val, bool nearClip = false )
{
	// Ye Olde Sutherland-Hodgman clipping algorithm
	int numOutVerts = 0;
	D3DXVECTOR3* pStart = ppVert[nVertexCount-1];
	bool startInside = PointInsidePlane( pStart, normalInd, val, nearClip );
	for (int i = 0; i < nVertexCount; ++i)
	{
		D3DXVECTOR3* pEnd = ppVert[i];
		bool endInside = PointInsidePlane( pEnd, normalInd, val, nearClip );
		if (endInside)
		{
			if (!startInside)
			{
				IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] );
				ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++];
			}
			ppOutVert[numOutVerts++] = pEnd;
		}
		else
		{
			if (startInside)
			{
				IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] );
				ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++];
			}
		}
		pStart = pEnd;
		startInside = endInside;
	}

	return numOutVerts;
}

void CTempMeshDX8::ClipTriangle( D3DXVECTOR3** ppVert, float zNear, D3DXMATRIX& projection )
{
	int i;
	int nVertexCount = 3;
	D3DXVECTOR3* ppClipVert1[10];
	D3DXVECTOR3* ppClipVert2[10];

	g_NumClipVerts = 0;

	// Clip against the near plane in view space to prevent negative w.
	// Clip against each plane
	nVertexCount = ClipTriangleAgainstPlane( ppVert, nVertexCount, ppClipVert1, 2, zNear, true );
	if (nVertexCount < 3)
		return;

	// Sucks that I have to do this, but I have to clip near plane in view space 
	// Clipping in projection space is screwy when w < 0
	// Transform the clipped points into projection space
	Assert( g_NumClipVerts <= 2 );
	for (i = 0; i < nVertexCount; ++i)
	{
		if (ppClipVert1[i] == &g_ClipVerts[0])
		{
			D3DXVec3TransformCoord( &g_ClipVerts[0], ppClipVert1[i], &projection ); 
		}
		else if (ppClipVert1[i] == &g_ClipVerts[1])
		{
			D3DXVec3TransformCoord( &g_ClipVerts[1], ppClipVert1[i], &projection ); 
		}
		else
		{
			D3DXVec3TransformCoord( &g_ClipVerts[g_NumClipVerts], ppClipVert1[i], &projection );
		    ppClipVert1[i] = &g_ClipVerts[g_NumClipVerts];
			++g_NumClipVerts;
		}
	}

	nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 2, 1.0f );
	if (nVertexCount < 3)
		return;

	nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 0, 1.0f );
	if (nVertexCount < 3)
		return;

	nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 0, -1.0f );
	if (nVertexCount < 3)
		return;

	nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 1, 1.0f );
	if (nVertexCount < 3)
		return;
	
	nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 1, -1.0f );
	if (nVertexCount < 3)
		return;

#ifdef DRAW_SELECTION
	if( 1 || g_bDrawSelection )
	{
		srand( *(int*)(&ppClipVert2[0]->x) ); 
		unsigned char r = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64;
		unsigned char g = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64;
		unsigned char b = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64;

		ShaderAPI()->SetupSelectionModeVisualizationState();

		CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder();
		IMesh* pMesh = GetDynamicMesh();
		pMeshBuilder->Begin( pMesh, MATERIAL_POLYGON, nVertexCount );
		
		for ( i = 0; i < nVertexCount; ++i)
		{
			pMeshBuilder->Position3fv( *ppClipVert2[i] );
			pMeshBuilder->Color3ub( r, g, b );
			pMeshBuilder->AdvanceVertex();
		}

		pMeshBuilder->End();
		pMesh->Draw();

		pMeshBuilder->Begin( pMesh, MATERIAL_LINE_LOOP, nVertexCount );
		
		for ( i = 0; i < nVertexCount; ++i)
		{
			pMeshBuilder->Position3fv( *ppClipVert2[i] );
			pMeshBuilder->Color3ub( 255, 255, 255 );
			pMeshBuilder->AdvanceVertex();
		}

		pMeshBuilder->End();
		pMesh->Draw();
	}
#endif

	// Compute closest and furthest verts
	float minz = ppClipVert2[0]->z;
	float maxz = ppClipVert2[0]->z;
	for ( i = 1; i < nVertexCount; ++i )
	{
		if (ppClipVert2[i]->z < minz)
			minz = ppClipVert2[i]->z;
		else if (ppClipVert2[i]->z > maxz)
			maxz = ppClipVert2[i]->z;
	}

	ShaderAPI()->RegisterSelectionHit( minz, maxz );
}

//-----------------------------------------------------------------------------
// Selection mode 
//-----------------------------------------------------------------------------
void CTempMeshDX8::TestSelection()
{
	// Note that this doesn't take into account any vertex modification
	// done in a vertex shader. Also it doesn't take into account any clipping
	// done in hardware

	// Blow off points and lines; they don't matter
	if ((m_Type != MATERIAL_TRIANGLES) && (m_Type != MATERIAL_TRIANGLE_STRIP))
		return;

	D3DXMATRIX modelToView, projection;
	ComputeModelToView( modelToView );
	ShaderAPI()->GetMatrix( MATERIAL_PROJECTION, (float*)&projection );
	float zNear = -projection.m[3][2] / projection.m[2][2];

	D3DXVECTOR3* pPos[3];
	D3DXVECTOR3 edge[2];
	D3DXVECTOR3 normal;

	int numTriangles;
	if (m_Type == MATERIAL_TRIANGLES)
		numTriangles = m_IndexData.Count() / 3;
	else
		numTriangles = m_IndexData.Count() - 2;

	float cullFactor = ComputeCullFactor();

	// Makes the lovely loop simpler
	if (m_Type == MATERIAL_TRIANGLE_STRIP)
		cullFactor *= -1.0f;

	// We'll need some temporary memory to tell us if we're transformed the vert
	int nVertexCount = m_VertexData.Count() / m_VertexSize;
	static CUtlVector< unsigned char > transformedVert;
	int transformedVertSize = (nVertexCount + 7) >> 3;
	transformedVert.RemoveAll();
	transformedVert.EnsureCapacity( transformedVertSize );
	transformedVert.AddMultipleToTail( transformedVertSize );
	memset( transformedVert.Base(), 0, transformedVertSize );

	int indexPos;
	for (int i = 0; i < numTriangles; ++i)
	{
		// Get the three indices
		if (m_Type == MATERIAL_TRIANGLES)
		{
			indexPos = i * 3;
		}
		else
		{
			Assert( m_Type == MATERIAL_TRIANGLE_STRIP );
			cullFactor *= -1.0f;
			indexPos = i;
		}

		// BAH. Gotta clip to the near clip plane in view space to prevent
		// negative w coords; negative coords throw off the projection-space clipper.

		// Get the three positions in view space
		int inFrontIdx = -1;
		for (int j = 0; j < 3; ++j)
		{
			int index = m_IndexData[indexPos];
			D3DXVECTOR3* pPosition = (D3DXVECTOR3*)&m_VertexData[index * m_VertexSize];
			if ((transformedVert[index >> 3] & (1 << (index & 0x7))) == 0)
			{
				D3DXVec3TransformCoord( pPosition, pPosition, &modelToView );
				transformedVert[index >> 3] |= (1 << (index & 0x7));
			}

			pPos[j] = pPosition;
			if (pPos[j]->z < 0.0f)
				inFrontIdx = j;
			++indexPos;
		}

		// all points are behind the camera
		if (inFrontIdx < 0)
			continue;

		// backface cull....
		D3DXVec3Subtract( &edge[0], pPos[1], pPos[0] );
		D3DXVec3Subtract( &edge[1], pPos[2], pPos[0] );
		D3DXVec3Cross( &normal, &edge[0], &edge[1] );
		float dot = D3DXVec3Dot( &normal, pPos[inFrontIdx] );
		if (dot * cullFactor > 0.0f)
			continue;

		// Clip to viewport
		ClipTriangle( pPos, zNear, projection );
	}
}

//-----------------------------------------------------------------------------
// Begins a render pass
//-----------------------------------------------------------------------------
void CTempMeshDX8::BeginPass( )
{
	Assert( !m_InPass );

#ifdef DBGFLAG_ASSERT
	m_InPass = true;
#endif

	CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder();

	CDynamicMeshDX8* pMesh = GetDynamicMesh( );

	int nIndexCount;
	int nFirstIndex;
	if ((s_FirstIndex == -1) && (s_NumIndices == 0))
	{
		nIndexCount = m_IndexData.Count();
		nFirstIndex = 0;
	}
	else
	{
		nIndexCount = s_NumIndices;
		nFirstIndex = s_FirstIndex;
	}
	
	int i;
	int nVertexCount = m_VertexData.Count() / m_VertexSize;
	pMeshBuilder->Begin( pMesh, m_Type, nVertexCount, nIndexCount );

	// Copy in the vertex data...
	// Note that since we pad the vertices, it's faster for us to simply
	// copy the fields we're using...
	Assert( pMeshBuilder->BaseVertexData() );
	memcpy( pMeshBuilder->BaseVertexData(), m_VertexData.Base(), m_VertexData.Count() );
	pMeshBuilder->AdvanceVertices( m_VertexData.Count() / m_VertexSize );

	for ( i = 0; i < nIndexCount; ++i )
	{
		pMeshBuilder->Index( m_IndexData[nFirstIndex+i] );
		pMeshBuilder->AdvanceIndex();
	}

	// NOTE: The client is expected to modify the data after this call is made
	pMeshBuilder->Reset();
}

//-----------------------------------------------------------------------------
// Draws a single pass
//-----------------------------------------------------------------------------
void CTempMeshDX8::RenderPass()
{
	Assert( m_InPass );

#ifdef DBGFLAG_ASSERT
	m_InPass = false;
#endif

	// Have the shader API modify the vertex data as it needs
	// This vertex data is modified based on state set by the material
	ShaderAPI()->ModifyVertexData( );

	// Done building the mesh
	ShaderAPI()->GetVertexModifyBuilder()->End();

	// Have the dynamic mesh render a single pass...
	GetDynamicMesh()->DrawSinglePassImmediately();
}

//-----------------------------------------------------------------------------
//
// Buffered mesh implementation
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------

CBufferedMeshDX8::CBufferedMeshDX8() : m_IsFlushing(false), m_WasRendered(true)
{
	m_pMesh = NULL;
#ifdef DEBUG_BUFFERED_STATE
	m_BufferedStateSet = false;
#endif
}

CBufferedMeshDX8::~CBufferedMeshDX8()
{
}


//-----------------------------------------------------------------------------
// Sets the mesh
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::SetMesh( CBaseMeshDX8* pMesh )
{
	if (m_pMesh != pMesh)
	{
		ShaderAPI()->FlushBufferedPrimitives();
		m_pMesh = pMesh;
	}
}


//-----------------------------------------------------------------------------
// Spews the mesh data
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc )
{
	if ( m_pMesh )
	{
		m_pMesh->Spew( nVertexCount, nIndexCount, spewDesc );
	}
}


//-----------------------------------------------------------------------------
// Sets the material
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::SetVertexFormat( VertexFormat_t format )
{
	Assert( m_pMesh );
	if (m_pMesh->NeedsVertexFormatReset(format))
	{
		ShaderAPI()->FlushBufferedPrimitives();
		m_pMesh->SetVertexFormat( format );
	}
}

void CBufferedMeshDX8::SetMorphFormat( MorphFormat_t format )
{
	Assert( m_pMesh );
	m_pMesh->SetMorphFormat( format );
}

VertexFormat_t CBufferedMeshDX8::GetVertexFormat( ) const
{
	Assert( m_pMesh );
	return m_pMesh->GetVertexFormat();
}

void CBufferedMeshDX8::SetMaterial( IMaterial* pMaterial )
{
#if _DEBUG
	Assert( m_pMesh );
	m_pMesh->SetMaterial( pMaterial );
#endif
}

void CBufferedMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & spewDesc )
{
#if _DEBUG
	Assert( m_pMesh );
	m_pMesh->ValidateData( nVertexCount, nIndexCount, spewDesc );
#endif
}


//-----------------------------------------------------------------------------
// Sets the flex mesh to render with this mesh
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes )
{
	// FIXME: Probably are situations where we don't need to flush,
	// but this is going to look different in a very short while, so I'm not going to bother
	ShaderAPI()->FlushBufferedPrimitives();
	m_pMesh->SetFlexMesh( pMesh, nVertexOffsetInBytes );
}


//-----------------------------------------------------------------------------
// checks to see if it was rendered..
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::ResetRendered()
{
	m_WasRendered = false;
}

bool CBufferedMeshDX8::WasNotRendered() const
{
	return !m_WasRendered;
}


//-----------------------------------------------------------------------------
// "Draws" it
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::Draw( int nFirstIndex, int nIndexCount )
{
	if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
	{
		m_WasRendered = true;
		MarkAsDrawn();
		return;
	}
	
	Assert( !m_IsFlushing && !m_WasRendered );

	// Gotta draw all of the buffered mesh
	Assert( (nFirstIndex == -1) && (nIndexCount == 0) );

	// No need to draw it more than once...
	m_WasRendered = true;

	// We've got something to flush
	m_FlushNeeded = true;

	// Less than 0 indices indicates we were using a standard buffer
	if ( m_pMesh->HasFlexMesh() || !ShaderUtil()->GetConfig().bBufferPrimitives )
	{
		ShaderAPI()->FlushBufferedPrimitives();
	}
}


//-----------------------------------------------------------------------------
// Sets the primitive mode
//-----------------------------------------------------------------------------

void CBufferedMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type )
{
	Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) );
	Assert( type != MATERIAL_HETEROGENOUS );

	if (type != GetPrimitiveType())
	{
		ShaderAPI()->FlushBufferedPrimitives();
		m_pMesh->SetPrimitiveType(type);
	}
}

MaterialPrimitiveType_t CBufferedMeshDX8::GetPrimitiveType( ) const
{
	return m_pMesh->GetPrimitiveType();
}


//-----------------------------------------------------------------------------
// Locks/unlocks the entire mesh
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	ShaderUtil()->SyncMatrices();

	Assert( m_pMesh );
	Assert( m_WasRendered );

	// Do some pre-lock processing
	m_pMesh->PreLock();

	// for tristrips, gotta make degenerate ones...
	m_ExtraIndices = 0;
	bool tristripFixup = (m_pMesh->IndexCount() != 0) && 
		(m_pMesh->GetPrimitiveType() == MATERIAL_TRIANGLE_STRIP);
	if (tristripFixup)
	{
		m_ExtraIndices = (m_pMesh->IndexCount() & 0x1) != 0 ? 3 : 2;
		nIndexCount += m_ExtraIndices;
	}

	// Flush if we gotta
	if (!m_pMesh->HasEnoughRoom(nVertexCount, nIndexCount))
	{
		ShaderAPI()->FlushBufferedPrimitives();
	}

	m_pMesh->LockMesh( nVertexCount, nIndexCount, desc );

//	This is taken care of in the function above.
//	CBaseMeshDX8::m_bMeshLocked = true;

	// Deal with fixing up the tristrip..
	if ( tristripFixup && desc.m_nIndexSize )
	{
		char buf[32];
		if (DebugTrace())
		{
			if (m_ExtraIndices == 3)
				sprintf(buf,"Link Index: %d %d\n", m_LastIndex, m_LastIndex);
			else
				sprintf(buf,"Link Index: %d\n", m_LastIndex);
			Plat_DebugString(buf);
		}
		*desc.m_pIndices++ = m_LastIndex;
		if (m_ExtraIndices == 3)
		{
			*desc.m_pIndices++ = m_LastIndex;
		}

		// Leave room for the last padding index
		++desc.m_pIndices;
	}

	m_WasRendered = false;

#ifdef DEBUG_BUFFERED_MESHES
	if (m_BufferedStateSet)
	{
		BufferedState_t compare;
		ShaderAPI()->GetBufferedState( compare );
		Assert( !memcmp( &compare, &m_BufferedState, sizeof(compare) ) );
	}
	else
	{
		ShaderAPI()->GetBufferedState( m_BufferedState );
		m_BufferedStateSet = true;
	}
#endif
}


//-----------------------------------------------------------------------------
// Locks/unlocks the entire mesh
//-----------------------------------------------------------------------------
void CBufferedMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
{
	Assert( m_pMesh );

	// Gotta fix up the first index to batch strips reasonably
	if ((m_pMesh->GetPrimitiveType() == MATERIAL_TRIANGLE_STRIP) && desc.m_nIndexSize )
	{
		if (m_ExtraIndices > 0)
		{
			*(desc.m_pIndices - 1) = *desc.m_pIndices;

			if (DebugTrace())
			{
				char buf[32];
				sprintf(buf,"Link Index: %d\n", *desc.m_pIndices);
				Plat_DebugString(buf);
			}
		}
		
		// Remember the last index for next time 
		m_LastIndex = desc.m_pIndices[nIndexCount - 1];

		nIndexCount += m_ExtraIndices;
	}

	m_pMesh->UnlockMesh( nVertexCount, nIndexCount, desc );

//	This is taken care of in the function above.
//	CBaseMeshDX8::m_bMeshLocked = false;
}


//-----------------------------------------------------------------------------
// Renders a pass
//-----------------------------------------------------------------------------

void CBufferedMeshDX8::RenderPass()
{
	// this should never be called!
	Assert(0);
}

//-----------------------------------------------------------------------------
// Flushes queued data
//-----------------------------------------------------------------------------

void CBufferedMeshDX8::Flush( )
{
	// If you are hitting this assert you are causing a flush between a 
	// meshbuilder begin/end and you are more than likely losing rendering data.
	AssertOnce( !CBaseMeshDX8::m_bMeshLocked );

	if ( m_pMesh && !m_IsFlushing && m_FlushNeeded )
	{
		VPROF( "CBufferedMeshDX8::Flush" );

#ifdef DEBUG_BUFFERED_MESHES
		if( m_BufferedStateSet )
		{
			BufferedState_t compare;
			ShaderAPI()->GetBufferedState( compare );
			Assert( !memcmp( &compare, &m_BufferedState, sizeof(compare) ) );
			m_BufferedStateSet = false;
		}
#endif

		m_IsFlushing = true;

		// Actually draws the data using the mesh's material
		static_cast<IMesh*>(m_pMesh)->Draw();

		m_IsFlushing = false;
		m_FlushNeeded = false;

		m_pMesh->SetFlexMesh( NULL, 0 );
	}
}

//-----------------------------------------------------------------------------
//
// Mesh manager implementation
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CMeshMgr::CMeshMgr() : 
	m_pDynamicIndexBuffer(0), 
	m_DynamicTempMesh(true), 
	m_pVertexIDBuffer(0),
	m_pCurrentVertexBuffer( NULL ),
	m_CurrentVertexFormat( 0 ),
	m_pCurrentIndexBuffer( NULL ),
	m_DynamicIndexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, MATERIAL_INDEX_FORMAT_16BIT, INDEX_BUFFER_SIZE, "dynamic" ),
	m_DynamicVertexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, VERTEX_FORMAT_UNKNOWN, DYNAMIC_VERTEX_BUFFER_MEMORY, "dynamic" )
{
	m_bUseFatVertices = false;
	m_nIndexBufferOffset = 0;
	memset( m_pVertexBufferOffset, 0, sizeof(m_pVertexBufferOffset) );
	memset( m_pCurrentVertexStride, 0, sizeof(m_pCurrentVertexStride) );
	memset( m_pFirstVertex, 0, sizeof(m_pFirstVertex) );
	memset( m_pVertexCount, 0, sizeof(m_pVertexCount) );
	m_nUnusedVertexFields = 0;
	m_nUnusedTextureCoords = 0;
	m_pZeroVertexBuffer = NULL;
}

CMeshMgr::~CMeshMgr()
{
}


//-----------------------------------------------------------------------------
// Initialize, shutdown
//-----------------------------------------------------------------------------
void CMeshMgr::Init()
{
	m_DynamicMesh.Init( 0 );
	m_DynamicFlexMesh.Init( 1 );

	CreateDynamicIndexBuffer();

	// If we're running in vs3.0, allocate a vertexID buffer
	CreateVertexIDBuffer();

	CreateZeroVertexBuffer();
		
	m_BufferedMode = !IsX360();
}

void CMeshMgr::Shutdown()
{
	CleanUp();
}


//-----------------------------------------------------------------------------
// Task switch...
//-----------------------------------------------------------------------------
void CMeshMgr::ReleaseBuffers()
{
	if ( IsPC() && mat_debugalttab.GetBool() )
	{
		Warning( "mat_debugalttab: CMeshMgr::ReleaseBuffers\n" );
	}

	CleanUp();
	m_DynamicMesh.Reset( );
	m_DynamicFlexMesh.Reset( );
}

void CMeshMgr::RestoreBuffers()
{
	if ( IsPC() && mat_debugalttab.GetBool() )
	{
		Warning( "mat_debugalttab: CMeshMgr::RestoreBuffers\n" );
	}
	Init();
}


//-----------------------------------------------------------------------------
// Cleans up vertex and index buffers
//-----------------------------------------------------------------------------
void CMeshMgr::CleanUp()
{
	DestroyDynamicIndexBuffer();

	DestroyVertexBuffers();

	// If we're running in vs3.0, allocate a vertexID buffer
	DestroyVertexIDBuffer();
	DestroyZeroVertexBuffer();
}

//-----------------------------------------------------------------------------
// Fills a vertexID buffer
//-----------------------------------------------------------------------------
void CMeshMgr::FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount )
{
	if ( IsX360() )
		return;

	// Fill the buffer with the values 0->(nCount-1)
	int nBaseVertexIndex = 0;
	float *pBuffer = (float*)pVertexIDBuffer->Lock( nCount, nBaseVertexIndex );	
	for ( int i = 0; i < nCount; ++i )
	{
		*pBuffer++ = (float)i;
	}
	pVertexIDBuffer->Unlock( nCount );
}

//-----------------------------------------------------------------------------
// Creates, destroys the dynamic index buffer
//-----------------------------------------------------------------------------
void CMeshMgr::CreateDynamicIndexBuffer()
{
	DestroyDynamicIndexBuffer();
	SafeAssign( &m_pDynamicIndexBuffer, new CIndexBuffer( Dx9Device(), INDEX_BUFFER_SIZE, ShaderAPI()->UsingSoftwareVertexProcessing(), true ) );
}

void CMeshMgr::DestroyDynamicIndexBuffer()
{
	SafeRelease( &m_pDynamicIndexBuffer );
}

//-----------------------------------------------------------------------------
// Creates, destroys the vertexID buffer
//-----------------------------------------------------------------------------
void CMeshMgr::CreateVertexIDBuffer()
{
	if ( IsX360() )
		return;

	DestroyVertexIDBuffer();

	// Track mesh allocations
	g_VBAllocTracker->TrackMeshAllocations( "CreateVertexIDBuffer" );
	if ( g_pHardwareConfig->HasFastVertexTextures() )
	{
		m_pVertexIDBuffer = new CVertexBuffer( Dx9Device(), 0, 0, sizeof(float), 
			VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() );
		FillVertexIDBuffer( m_pVertexIDBuffer, VERTEX_BUFFER_SIZE );
	}
	g_VBAllocTracker->TrackMeshAllocations( NULL );
}

void CMeshMgr::CreateZeroVertexBuffer()
{
	if ( !m_pZeroVertexBuffer )
	{
		// In GL glVertexAttribPointer() doesn't support strides of 0, so we need to allocate a dummy vertex buffer large enough to handle 16-bit indices with a stride of 4 byte per vertex, plus a bit more for safety (in case basevertexindex is > 0).
		// We could also try just disabling any vertex attribs that fetch from stream 2 and need 0's, but AMD reports this could hit a slow path in the driver. Argh.
		uint nBufSize = IsOpenGL() ? ( 65536 * 2 * 4 ) : 4096;
		HRESULT hr = Dx9Device()->CreateVertexBuffer( nBufSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_pZeroVertexBuffer, NULL );
		if ( !FAILED( hr ) )
		{
			void *pData = NULL;
			m_pZeroVertexBuffer->Lock( 0, nBufSize, &pData, D3DLOCK_NOSYSLOCK );
			if ( pData )
			{
				V_memset( pData, 0, nBufSize );
				m_pZeroVertexBuffer->Unlock();
			}
		}
	}
}

void CMeshMgr::DestroyZeroVertexBuffer()
{
	if ( m_pZeroVertexBuffer )
	{
		m_pZeroVertexBuffer->Release();
		m_pZeroVertexBuffer = NULL;
	}
}

void CMeshMgr::DestroyVertexIDBuffer()
{
	if ( m_pVertexIDBuffer )
	{
		delete m_pVertexIDBuffer;
		m_pVertexIDBuffer = NULL;
	}
}

CVertexBuffer *CMeshMgr::GetVertexIDBuffer( )
{
	return m_pVertexIDBuffer;
}


//-----------------------------------------------------------------------------
// Unused vertex fields
//-----------------------------------------------------------------------------
void CMeshMgr::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords )
{
	m_nUnusedVertexFields = nFlags;
	m_nUnusedTextureCoords = 0;
	for ( int i = 0; i < nTexCoordCount; ++i )
	{
		if ( pUnusedTexCoords[i] )
		{
			m_nUnusedTextureCoords |= ( 1 << i );
		}
	}
}


//-----------------------------------------------------------------------------
// Is the mesh dynamic?
//-----------------------------------------------------------------------------
bool CMeshMgr::IsDynamicMesh( IMesh* pMesh ) const
{
	return ( pMesh == &m_DynamicMesh ) || ( pMesh == &m_DynamicFlexMesh );
}

bool CMeshMgr::IsBufferedDynamicMesh( IMesh* pMesh ) const
{
	return ( pMesh == &m_BufferedMesh );
}

bool CMeshMgr::IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const
{
	return ( pVertexBuffer == &m_DynamicVertexBuffer );
}

bool CMeshMgr::IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const
{
	return ( pIndexBuffer == &m_DynamicIndexBuffer );
}

//-----------------------------------------------------------------------------
// Discards the dynamic vertex and index buffer
//-----------------------------------------------------------------------------
void CMeshMgr::DiscardVertexBuffers()
{
	VPROF_BUDGET( "CMeshMgr::DiscardVertexBuffers", VPROF_BUDGETGROUP_SWAP_BUFFERS );
	// This shouldn't be necessary, but it seems to be on GeForce 2
	// It helps when running WC and the engine simultaneously.
	ResetMeshRenderState();

	if ( !g_pShaderDeviceDx8->IsDeactivated() )
	{
		for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; )
		{
			m_DynamicVertexBuffers[i].m_pBuffer->FlushAtFrameStart();
		}
		m_pDynamicIndexBuffer->FlushAtFrameStart();
	}
}


//-----------------------------------------------------------------------------
// Releases all dynamic vertex buffers
//-----------------------------------------------------------------------------
void CMeshMgr::DestroyVertexBuffers()
{
	// Necessary for cleanup
	RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
	RECORD_INT( -1 );
	RECORD_INT( 0 );
	RECORD_INT( 0 );
	RECORD_INT( 0 );
	D3DSetStreamSource( 0, 0, 0, 0 );

	RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
	RECORD_INT( -1 );
	RECORD_INT( 1 );
	RECORD_INT( 0 );
	RECORD_INT( 0 );
	D3DSetStreamSource( 1, 0, 0, 0 );

	RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
	RECORD_INT( -1 );
	RECORD_INT( 2 );
	RECORD_INT( 0 );
	RECORD_INT( 0 );
	D3DSetStreamSource( 2, 0, 0, 0 );

#ifndef _X360
	RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
	RECORD_INT( -1 );
	RECORD_INT( 3 );
	RECORD_INT( 0 );
	RECORD_INT( 0 );
	D3DSetStreamSource( 3, 0, 0, 0 );
#endif

	for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; )
	{
		if (m_DynamicVertexBuffers[i].m_pBuffer)
		{
			delete m_DynamicVertexBuffers[i].m_pBuffer;
		}
	}
	m_DynamicVertexBuffers.RemoveAll();
	m_DynamicMesh.Reset();
	m_DynamicFlexMesh.Reset();
}


//-----------------------------------------------------------------------------
// Flushes the dynamic mesh
//-----------------------------------------------------------------------------
void CMeshMgr::Flush()
{
	if ( IsPC() )
	{
		m_BufferedMesh.HandleLateCreation();
		m_BufferedMesh.Flush();
	}
}


//-----------------------------------------------------------------------------
// Creates, destroys static meshes
//-----------------------------------------------------------------------------
IMesh* CMeshMgr::CreateStaticMesh( VertexFormat_t format, const char *pTextureBudgetGroup, IMaterial * pMaterial )
{
	// FIXME: Use a fixed-size allocator
	CMeshDX8* pNewMesh = new CMeshDX8( pTextureBudgetGroup );
	pNewMesh->SetVertexFormat( format );
	if ( pMaterial != NULL )
	{
		pNewMesh->SetMorphFormat( pMaterial->GetMorphFormat() );
		pNewMesh->SetMaterial( pMaterial );
	}
	return pNewMesh;
}

void CMeshMgr::DestroyStaticMesh( IMesh* pMesh )
{
	// Don't destroy the dynamic mesh!
	Assert( !IsDynamicMesh( pMesh ) );
	CBaseMeshDX8* pMeshImp = static_cast<CBaseMeshDX8*>(pMesh);
	if (pMeshImp)
	{
		delete pMeshImp;
	}
}

//-----------------------------------------------------------------------------
// Gets at the *real* dynamic mesh
//-----------------------------------------------------------------------------
IMesh* CMeshMgr::GetActualDynamicMesh( VertexFormat_t format )
{
	m_DynamicMesh.SetVertexFormat( format );
	return &m_DynamicMesh;
}

//-----------------------------------------------------------------------------
// Copy a static mesh index buffer to a dynamic mesh index buffer
//-----------------------------------------------------------------------------
void CMeshMgr::CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh,
															   CMeshDX8 *pSrcIndexMesh )
{
	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );

	Assert( !pSrcIndexMesh->IsDynamic() );
	int nIndexCount = pSrcIndexMesh->IndexCount();
	
	CMeshBuilder dstMeshBuilder;
	dstMeshBuilder.Begin( pDstIndexMesh, pSrcIndexMesh->GetPrimitiveType(), 0, nIndexCount );
	CIndexBuffer *srcIndexBuffer = pSrcIndexMesh->GetIndexBuffer();
	int dummy = 0;
	unsigned short *srcIndexArray = srcIndexBuffer->Lock( false, nIndexCount, dummy, 0 );
	int i;
	for( i = 0; i < nIndexCount; i++ )
	{
		dstMeshBuilder.Index( srcIndexArray[i] );
		dstMeshBuilder.AdvanceIndex();
	}
	srcIndexBuffer->Unlock( 0 );
	dstMeshBuilder.End();
}


IMesh *CMeshMgr::GetFlexMesh()
{
	if ( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() )
	{
		// FIXME: Kinda ugly size.. 28 bytes
		m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_WRINKLE | VERTEX_FORMAT_USE_EXACT_FORMAT );
	}
	else
	{
		// Same size as a pair of float3s (24 bytes)
		m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_USE_EXACT_FORMAT );
	}
	return &m_DynamicFlexMesh;
}

//-----------------------------------------------------------------------------
// Gets at the dynamic mesh
//-----------------------------------------------------------------------------
IMesh* CMeshMgr::GetDynamicMesh( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount,
	bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride )
{
	tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );

	Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );

	if ( IsX360() )
	{
		buffered = false;
	}

	// Can't be buffered if we're overriding the buffers
	if ( pVertexOverride || pIndexOverride )
	{
		buffered = false;
	}

	// When going from buffered to unbuffered mode, need to flush..
	if ((m_BufferedMode != buffered) && m_BufferedMode)
	{
		m_BufferedMesh.SetMesh(0);
	}
	m_BufferedMode = buffered;

	IMaterialInternal* pMatInternal = static_cast<IMaterialInternal*>(pMaterial);

	bool needTempMesh = ShaderAPI()->IsInSelectionMode();

#ifdef DRAW_SELECTION
	if( g_bDrawSelection )
	{
		needTempMesh = true;
	}
#endif

	CBaseMeshDX8* pMesh;

	if ( needTempMesh )
	{
		// These haven't been implemented yet for temp meshes!
		// I'm not a hundred percent sure how to implement them; it would
		// involve a lock and a copy at least, which would stall the entire
		// rendering pipeline.
		Assert( !pVertexOverride );
		
		if( pIndexOverride )
		{
			CopyStaticMeshIndexBufferToTempMeshIndexBuffer( &m_DynamicTempMesh,
				( CMeshDX8 * )pIndexOverride );
		}
		pMesh = &m_DynamicTempMesh;
	}
	else
	{
		pMesh = &m_DynamicMesh;
	}

	if ( m_BufferedMode )
	{
		Assert( !m_BufferedMesh.WasNotRendered() );
		m_BufferedMesh.SetMesh( pMesh );
		pMesh = &m_BufferedMesh;
	}

	if( !pVertexOverride )
	{
		// Remove VERTEX_FORMAT_COMPRESSED from the material's format (dynamic meshes don't
		// support compression, and all materials should support uncompressed verts too)
		VertexFormat_t materialFormat = pMatInternal->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED;
		VertexFormat_t fmt = ( vertexFormat != 0 ) ? vertexFormat : materialFormat;
		if ( vertexFormat != 0 )
		{
			int nVertexFormatBoneWeights = NumBoneWeights( vertexFormat );
			if ( nHWSkinBoneCount < nVertexFormatBoneWeights )
			{
				nHWSkinBoneCount = nVertexFormatBoneWeights;
			}
		}

		// Force the requested number of bone weights
		fmt &= ~VERTEX_BONE_WEIGHT_MASK;
		if ( nHWSkinBoneCount > 0 )
		{
			fmt |= VERTEX_BONEWEIGHT( 2 );
			fmt |= VERTEX_BONE_INDEX;
		}

		pMesh->SetVertexFormat( fmt );
	}
	else
	{
		CBaseMeshDX8 *pDX8Mesh = static_cast<CBaseMeshDX8*>(pVertexOverride);
		pMesh->SetVertexFormat( pDX8Mesh->GetVertexFormat() );
	}
	pMesh->SetMorphFormat( pMatInternal->GetMorphFormat() );
	pMesh->SetMaterial( pMatInternal );

	// Note this works because we're guaranteed to not be using a buffered mesh
	// when we have overrides on
	// FIXME: Make work for temp meshes
	if ( pMesh == &m_DynamicMesh )
	{
		CBaseMeshDX8* pBaseVertex = static_cast<CBaseMeshDX8*>( pVertexOverride );
		if ( pBaseVertex )
		{
			m_DynamicMesh.OverrideVertexBuffer( pBaseVertex->GetVertexBuffer() );
		}

		CBaseMeshDX8* pBaseIndex = static_cast<CBaseMeshDX8*>( pIndexOverride );
		if ( pBaseIndex )
		{
			m_DynamicMesh.OverrideIndexBuffer( pBaseIndex->GetIndexBuffer() );
		}
	}

	return pMesh;
}


//-----------------------------------------------------------------------------
// Used to construct vertex data
//-----------------------------------------------------------------------------
void CMeshMgr::ComputeVertexDescription( unsigned char* pBuffer, 
	VertexFormat_t vertexFormat, MeshDesc_t& desc ) const
{
	ComputeVertexDesc( pBuffer, vertexFormat, (VertexDesc_t &)desc );
}


//-----------------------------------------------------------------------------
// Computes the vertex format
//-----------------------------------------------------------------------------
VertexFormat_t CMeshMgr::ComputeVertexFormat( unsigned int flags, 
			int nTexCoordArraySize, int* pTexCoordDimensions, int numBoneWeights,
			int userDataSize ) const
{
	// Construct a bitfield that makes sense and is unique from the standard FVF formats
	VertexFormat_t fmt = flags & ~VERTEX_FORMAT_USE_EXACT_FORMAT;

	if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE )
	{
		// Vertex compression is disabled - make sure all materials
		// say "No!" to compressed verts ( tested in IsValidVertexFormat() )
		fmt &= ~VERTEX_FORMAT_COMPRESSED;
	}

	// This'll take 3 bits at most
	Assert( numBoneWeights <= 4 );

	if ( numBoneWeights > 0 )
	{
		fmt |= VERTEX_BONEWEIGHT( 2 ); // Always exactly two weights
	}

	// Size is measured in # of floats
	Assert( userDataSize <= 4 );
	fmt |= VERTEX_USERDATA_SIZE(userDataSize);

	// NOTE: If pTexCoordDimensions isn't specified, then nTexCoordArraySize
	// is interpreted as meaning that we have n 2D texcoords in the first N texcoord slots
	nTexCoordArraySize = min( nTexCoordArraySize, (int)VERTEX_MAX_TEXTURE_COORDINATES );
	for ( int i = 0; i < nTexCoordArraySize; ++i )
	{
		if ( pTexCoordDimensions )
		{
			Assert( pTexCoordDimensions[i] >= 0 && pTexCoordDimensions[i] <= 4 );
			fmt |= VERTEX_TEXCOORD_SIZE( (TextureStage_t)i, pTexCoordDimensions[i] );
		}
		else 
		{
			fmt |= VERTEX_TEXCOORD_SIZE( (TextureStage_t)i, 2 );
		}
	}

	return fmt;
}


//-----------------------------------------------------------------------------
// Use fat vertices (for tools)
//-----------------------------------------------------------------------------
void CMeshMgr::UseFatVertices( bool bUseFat )
{
	m_bUseFatVertices = bUseFat;
}


//-----------------------------------------------------------------------------
// Returns the number of vertices we can render using the dynamic mesh
//-----------------------------------------------------------------------------
void CMeshMgr::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices )
{
	CBaseMeshDX8 *pBaseMesh = static_cast<CBaseMeshDX8*>( pMesh );
	if ( !pBaseMesh )
	{
		*pMaxVerts = 0;
		*pMaxIndices = m_pDynamicIndexBuffer->IndexCount();
		return;
	}

	if ( IsBufferedDynamicMesh( pMesh ) )
	{
		pBaseMesh = (CBaseMeshDX8*)static_cast<CBufferedMeshDX8*>( pBaseMesh )->GetMesh();
		pMesh = pBaseMesh;
	}

	// Static mesh? Max you can use is 65535
	if ( !IsDynamicMesh( pMesh ) )
	{
		*pMaxVerts = 65535;
		*pMaxIndices = 65535;
		return;
	}

	CVertexBuffer *pVertexBuffer = pBaseMesh->GetVertexBuffer();
	CIndexBuffer *pIndexBuffer = pBaseMesh->GetIndexBuffer();

	if ( !pVertexBuffer )
	{
		*pMaxVerts = 0;
		*pMaxIndices = 0;
		return;
	}

	if ( !bMaxUntilFlush )
	{
		*pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize();
		if ( *pMaxVerts > 65535 )
		{
			*pMaxVerts = 65535;
		}
		*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0;
		return;
	}

	*pMaxVerts = pVertexBuffer->NumVerticesUntilFlush();
	*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() - pIndexBuffer->IndexPosition() : 0;
	if ( *pMaxVerts == 0 )
	{
		*pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize();
	}
	if ( *pMaxVerts > 65535 )
	{
		*pMaxVerts = 65535;
	}
	if ( *pMaxIndices == 0 )
	{
		*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0;
	}
}

int CMeshMgr::GetMaxVerticesToRender( IMaterial *pMaterial )
{
	Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );

	// Be conservative, assume no compression (in here, we don't know if the caller will used a compressed VB or not)
	// FIXME: allow the caller to specify which compression type should be used to compute size from the vertex format
	//        (this can vary between multiple VBs/Meshes using the same material)
	VertexFormat_t fmt = pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED;
	int nVertexSize = VertexFormatSize( fmt );
	if ( nVertexSize == 0 )
	{
		// unable to determine vertex format information, possibly due to device loss.
		Warning( "bad vertex size for material %s\n", pMaterial->GetName() );
		return 0;
	}

	int nMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / nVertexSize;
	return MIN( nMaxVerts, 65535 );
}

int CMeshMgr::GetMaxIndicesToRender( )
{
	return INDEX_BUFFER_SIZE;
}

//-----------------------------------------------------------------------------
// Returns a vertex buffer appropriate for the flags
//-----------------------------------------------------------------------------
CVertexBuffer *CMeshMgr::FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t vertexFormat )
{
	int vertexSize = VertexFormatSize( vertexFormat );

	while ( m_DynamicVertexBuffers.Count() <= nDynamicBufferId )
	{
		// Track VB allocations (override any prior allocator string set higher up on the callstack)
		g_VBAllocTracker->TrackMeshAllocations( NULL );
		g_VBAllocTracker->TrackMeshAllocations( "CMeshMgr::FindOrCreateVertexBuffer (dynamic VB)" );

		// create the single 1MB dynamic vb that will be shared amongst all consumers
		// the correct thing is to use the largest expected vertex format size of max elements, but this
		// creates an undesirably large buffer - instead create the buffer we want, and fix consumers that bork
		// NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions
		int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize();
		int nIndex = m_DynamicVertexBuffers.AddToTail();
		m_DynamicVertexBuffers[nIndex].m_VertexSize = 0;
		m_DynamicVertexBuffers[nIndex].m_pBuffer = new CVertexBuffer( Dx9Device(), 0, 0, 
			nBufferMemory / VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing(), true ); 

		g_VBAllocTracker->TrackMeshAllocations( NULL );
	}
	
	if ( m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize != vertexSize )
	{
		// provide caller with dynamic vb in expected format
		// NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions
		int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize();
		m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize = vertexSize;
		m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer->ChangeConfiguration( vertexSize, nBufferMemory );

		// size changed means stream stride needs update
		// mark cached stream state as invalid to reset stream
		if ( nDynamicBufferId == 0 )
		{
			g_pLastVertex = NULL;
		}
	}

	return m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer;
}

CIndexBuffer *CMeshMgr::GetDynamicIndexBuffer()
{
	return m_pDynamicIndexBuffer;
}

IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered )
{
	Assert( 0 );
	return NULL;
//	return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL );
}

IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered )
{
	Assert( 0 );
	return NULL;
//	return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL );
}


//-----------------------------------------------------------------------------
IVertexBuffer *CMeshMgr::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup )
{
	// FIXME: Use a fixed-size allocator
	CVertexBufferDx8 *pNewVertexBuffer = new CVertexBufferDx8( type, fmt, nVertexCount, pBudgetGroup );
	return pNewVertexBuffer;
}

IIndexBuffer *CMeshMgr::CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup )
{
	switch( bufferType )
	{
	case SHADER_BUFFER_TYPE_STATIC:
	case SHADER_BUFFER_TYPE_DYNAMIC:
		{
			CIndexBufferDx8 *pIndexBuffer = new CIndexBufferDx8( bufferType, fmt, nIndexCount, pBudgetGroup );
			return pIndexBuffer;
		}
	case SHADER_BUFFER_TYPE_STATIC_TEMP:
	case SHADER_BUFFER_TYPE_DYNAMIC_TEMP:
		Assert( 0 );
		return NULL;
	default:
		Assert( 0 );
		return NULL;
	}
}

void CMeshMgr::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer )
{
	if ( pVertexBuffer && !IsDynamicVertexBuffer( pVertexBuffer ) )
	{
		delete pVertexBuffer;
	}
}

void CMeshMgr::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer )
{
	if ( pIndexBuffer && !IsDynamicIndexBuffer( pIndexBuffer ) )
	{
		delete pIndexBuffer;
	}
}

// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams?
IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered )
{
	if ( IsX360() )
	{
		bBuffered = false;
	}

	if ( CompressionType( vertexFormat ) != VERTEX_COMPRESSION_NONE )
	{
		// UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing)
		DebuggerBreak();
		return NULL;
	}

	// MESHFIXME
#if 0
	if ( ( m_BufferedMode != bBuffered ) && m_BufferedMode )
	{
		m_BufferedIndexBuffer.SetIndexBuffer( NULL );
	}
#endif

	m_BufferedMode = bBuffered;
	Assert( !m_BufferedMode ); // MESHFIXME: don't deal with buffered VBs yet.

	bool needTempMesh = ShaderAPI()->IsInSelectionMode();
	
#ifdef DRAW_SELECTION
	if( g_bDrawSelection )
	{
		needTempMesh = true;
	}
#endif

	Assert( !needTempMesh ); // MESHFIXME: don't support temp meshes here yet.

	CVertexBufferDx8 *pVertexBuffer;

	if ( needTempMesh )
	{
		Assert( 0 ); // MESHFIXME: don't do this yet.
//		pVertexBuffer = &m_DynamicTempVertexBuffer;
		pVertexBuffer = NULL;
	}
	else
	{
		pVertexBuffer = &m_DynamicVertexBuffer;
	}

	if ( m_BufferedMode )
	{
		Assert( 0 ); // don't support this yet.
#if 0
		Assert( !m_BufferedMesh.WasNotRendered() );
		m_BufferedMesh.SetMesh( pMesh );
		pMesh = &m_BufferedMesh;
#endif
	}

	return pVertexBuffer;
}

IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered )
{
	if ( IsX360() )
	{
		bBuffered = false;
	}

	m_BufferedMode = bBuffered;

	Assert( !m_BufferedMode );

#ifdef DBGFLAG_ASSERT
	bool needTempMesh = 
#endif
		ShaderAPI()->IsInSelectionMode();

#ifdef DRAW_SELECTION
	if( g_bDrawSelection )
	{
		needTempMesh = true;
	}
#endif

	Assert( !needTempMesh ); // don't handle this yet. MESHFIXME

	CIndexBufferBase *pIndexBuffer = &m_DynamicIndexBuffer;
	return pIndexBuffer;
}

void CMeshMgr::SetVertexIDStreamState()
{
	if ( IsX360() )
		return;

	// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
	// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
	bool bUsingVertexID = false;//IsUsingVertexID();
//	if ( bUsingVertexID != g_bUsingVertexID )
	{
		if ( bUsingVertexID )
		{
			// NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH
			// It's because the indices (which are not 0 based for dynamic buffers)
			// are accessing both the vertexID buffer + the regular vertex buffer.
			// This *might* be fixable with baseVertexIndex?
			Assert( !m_pCurrentVertexBuffer->IsDynamic() );

			CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( );
			RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
			RECORD_INT( pVertexIDBuffer->UID() );
			RECORD_INT( 3 );
			RECORD_INT( 0 );
			RECORD_INT( pVertexIDBuffer->VertexSize() );

			D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() );
			pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
		}
		else
		{
			RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
			RECORD_INT( -1 );	// vertex buffer id
			RECORD_INT( 3 );	// stream
			RECORD_INT( 0 );	// vertex offset
			RECORD_INT( 0 );	// vertex size

			D3DSetStreamSource( 3, 0, 0, 0 );
		}
		g_bUsingVertexID = bUsingVertexID;
	}
}

void CMeshMgr::SetColorStreamState()
{
	if ( g_pLastColorMesh )
	{
		RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
		RECORD_INT( -1 );	// vertex buffer id
		RECORD_INT( 1 );	// stream
		RECORD_INT( 0 );	// vertex offset
		RECORD_INT( 0 );	// vertex size

		D3DSetStreamSource( 1, 0, 0, 0 );
	}
	g_pLastColorMesh = NULL;
	g_nLastColorMeshVertOffsetInBytes = 0;
}

void CMeshMgr::SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride )
{
	// Calls in here assume shader support...
	if ( HardwareConfig()->SupportsVertexAndPixelShaders() )
	{
		// Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 0 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia)
		// togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency.
		D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 );

		// cFlexScale.x masks flex in vertex shader
		if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 )
		{
			float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
			ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
		}

		g_bFlexMeshStreamSet = false;
	}

	// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
	if ( g_pLastVertex || ( g_pLastVertexBuffer != m_pCurrentVertexBuffer->GetDx9Buffer() ) || 
		( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) || ( g_nLastVertStride != nVertexStride ))
	{
		Assert( m_pCurrentVertexBuffer && m_pCurrentVertexBuffer->GetDx9Buffer() );

		D3DSetStreamSource( 0, m_pCurrentVertexBuffer->GetDx9Buffer(), nVertOffsetInBytes, nVertexStride );
		m_pCurrentVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );

		g_pLastVertex = NULL;
		g_nLastVertStride = nVertexStride;
		g_pLastVertexBuffer = m_pCurrentVertexBuffer->GetDx9Buffer();
		g_nLastVertOffsetInBytes = nVertOffsetInBytes;
	}
}

bool CMeshMgr::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride )
{
	// Can't set the state if we're deactivated
	if ( g_pShaderDeviceDx8->IsDeactivated() )
	{
		ResetMeshRenderState();
		return false;
	}

	// make sure the vertex format is a superset of the current material's
	// vertex format...
	// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
#if 0
	// FIXME
	if ( !IsValidVertexFormat( vertexFormat ) )
	{
		Warning( "Material %s is being applied to a model, you need $model=1 in the .vmt file!\n",
			ShaderAPI()->GetBoundMaterial()->GetName() );
		return false;
	}
#endif

	SetVertexIDStreamState();
	SetColorStreamState();
	SetVertexStreamState( nVertexOffsetInBytes, nVertexStride );
	SetIndexStreamState( nFirstVertexIdx );

	return true;
}

void CMeshMgr::BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions )
{
	// FIXME: Multiple stream support isn't implemented yet
	Assert( nStreamID == 0 );

	m_pCurrentVertexBuffer = static_cast< CVertexBufferDx8 * >( pVertexBuffer ); 
	m_CurrentVertexFormat = fmt;
	m_pVertexBufferOffset[nStreamID] = nOffsetInBytes;
	m_pCurrentVertexStride[nStreamID] = m_pCurrentVertexBuffer->VertexSize();
	m_pFirstVertex[nStreamID] = nFirstVertex;
	m_pVertexCount[nStreamID] = nVertexCount, 
	m_pVertexIDBuffer = NULL;
}

void CMeshMgr::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes )
{
	m_pCurrentIndexBuffer = static_cast< CIndexBufferBase * >( pIndexBuffer );
	m_nIndexBufferOffset = nOffsetInBytes;
}

void CMeshMgr::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount )
{
	// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
	// make sure we aren't using a morph stream for this path.
//	Assert( !IsUsingMorphData() );
//	Assert( !m_pColorMesh );

	SetRenderState( m_pVertexBufferOffset[0], /* nFirstVertexIdx */0, m_CurrentVertexFormat, m_pCurrentVertexStride[0] );

	m_PrimitiveType = MATERIAL_TRIANGLES;
	Assert( primitiveType == MATERIAL_TRIANGLES );

	m_nFirstIndex = nFirstIndex;
	m_nNumIndices = nIndexCount;

	ShaderAPI()->DrawWithVertexAndIndexBuffers();
}

void CMeshMgr::RenderPassWithVertexAndIndexBuffers( void )
{
//	LOCK_SHADERAPI(); MESHFIXME
	VPROF( "CShaderAPIDX8::RenderPassWithVertexAndIndexBuffers" );

	Assert( m_PrimitiveType != MATERIAL_HETEROGENOUS );

//	for ( int iPrim=0; iPrim < s_nPrims; iPrim++ )
	{
//		CPrimList *pPrim = &s_pPrims[iPrim];

//		if ( pPrim->m_NumIndices == 0 )
//			continue;

		if ( m_PrimitiveType == MATERIAL_POINTS )
		{
			// (For point lists, we don't actually fill in indices, but we treat it as
			// though there are indices for the list up until here).
			Assert( 0 );
//			Dx9Device()->DrawPrimitive( ComputeMode( m_PrimitiveType ), s_FirstVertex, pPrim->m_NumIndices );
		}
		else
		{
//			int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices );

//			Warning( "CMeshMgr::RenderPassWithVertexAndIndexBuffers: DrawIndexedPrimitive: m_nFirstIndex = %d numPrimitives = %d\n", ( int )( ( CDynamiCIndexBufferDx8 * )m_pCurrentIndexBuffer )->m_FirstIndex, ( int )( m_nNumIndices / 3 ) );
			{
				VPROF( "Dx9Device()->DrawIndexedPrimitive" );
//				VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
//				VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives );

//				Dx9Device()->DrawIndexedPrimitive( 
//					m_Mode,
//					m_FirstIndex,
//					s_FirstVertex, 
//					s_NumVertices,
//					pPrim->m_FirstIndex,
//					numPrimitives );

				Assert( m_nFirstIndex >= 0 );

#ifdef CHECK_INDICES
				// g_pLastVertex - this is the current vertex buffer
				// g_pLastColorMesh - this is the curent color mesh, if there is one.
				// g_pLastIndex - this is the current index buffer.
				// vertoffset : m_FirstIndex
				CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8 * >( m_pCurrentIndexBuffer );
				if( m_PrimitiveType == MATERIAL_TRIANGLES || m_PrimitiveType == MATERIAL_TRIANGLE_STRIP )
				{
					// FIXME: need to be able to deal with multiple stream here, but don't bother for now.
					int j;
					int numVerts = m_pVertexCount[0];
					for( j = 0; j < m_nNumIndices; j++ )
					{
						int index = pIndexBuffer->GetShadowIndex( j + m_nFirstIndex );
						Assert( index >= m_pFirstVertex[0] );
						Assert( index < m_pFirstVertex[0] + numVerts );
					}
				}
#endif // CHECK_INDICES
				Dx9Device()->DrawIndexedPrimitive( 
					ComputeMode( m_PrimitiveType ),		// Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method.

					/*m_FirstIndex*/ 0,					// Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer.

					/*s_FirstVertex*/ m_pFirstVertex[0],// Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex.
														// This is zero for now since we don't do more than one batch yet with the new mesh interface.

					/*s_NumVertices*/ m_pVertexCount[0], 
														// Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex.
														// This is simple the number of verts in the current vertex buffer for now since we don't do more than one batch with the new mesh interface.

					m_nFirstIndex /*pPrim->m_FirstIndex*/,	// Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer.

					m_nNumIndices / 3/*numPrimitives*/  // Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type.
					);

				Assert( CMeshDX8::s_FirstVertex == 0 );
				Assert( CMeshDX8::s_NumVertices == 0 );
			}
		}
	}
}

//-----------------------------------------------------------------------------
void CMeshMgr::SetIndexStreamState( int firstVertexIdx )
{
	CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8* >( m_pCurrentIndexBuffer );
	IDirect3DIndexBuffer9 *pDx9Buffer = pIndexBuffer ? pIndexBuffer->GetDx9Buffer() : NULL;
	if ( g_pLastIndex || g_pLastIndexBuffer != pDx9Buffer )
	{
		Dx9Device()->SetIndices( pDx9Buffer );
		pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );

		g_pLastIndexBuffer = pDx9Buffer;
		SafeRelease( &g_pLastIndex );
		g_LastVertexIdx = -1;
	}
}