diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /materialsystem/shaderapidx9/meshdx8.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'materialsystem/shaderapidx9/meshdx8.cpp')
| -rw-r--r-- | materialsystem/shaderapidx9/meshdx8.cpp | 5980 |
1 files changed, 5980 insertions, 0 deletions
diff --git a/materialsystem/shaderapidx9/meshdx8.cpp b/materialsystem/shaderapidx9/meshdx8.cpp new file mode 100644 index 0000000..9a4af9e --- /dev/null +++ b/materialsystem/shaderapidx9/meshdx8.cpp @@ -0,0 +1,5980 @@ +//========= 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; + } +} + + |