|
|
//===== Copyright (c) Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//==================================================================//
#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"
#include "smartptr.h"
// fixme - stick this in a header file.
#if defined( _DEBUG ) && !defined( _GAMECONSOLE )
// 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"
#ifdef _GAMECONSOLE
#define MAX_TEMP_BUFFER 3
static int s_nMemoryFrame; static CMemoryStack s_BufferMemory[MAX_TEMP_BUFFER];
void *AllocateTempBuffer( size_t nSizeInBytes ) { return s_BufferMemory[s_nMemoryFrame].Alloc( nSizeInBytes, true ); }
#endif // _GAMECONSOLE
//-----------------------------------------------------------------------------
void FailedLock( const char *pszMsg ) { if ( IsPC() ) { Error( "%s", pszMsg ); } else { Warning( "%s", pszMsg ); } g_pMemAlloc->OutOfMemory(); }
#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
// NOTE: Using 6 here since we don't want extra checks in CIndexBuilder::FastQuad
static unsigned int g_pScratchIndexBuffer[6]; // shove indices into this if you don't actually want indices
// used to hold instance data when drawing multiple instances
static const MeshInstanceData_t *g_pInstanceData = NULL; static CompiledLightingState_t *g_pInstanceCompiledState = NULL; static InstanceInfo_t *g_pInstanceInfo = NULL; static int g_nInstanceCount = 0; #ifdef _DEBUG
int CVertexBuffer::s_BufferCount = 0; int CIndexBuffer::s_BufferCount = 0; #endif
//-----------------------------------------------------------------------------
// Important enumerations
//-----------------------------------------------------------------------------
enum { VERTEX_BUFFER_SIZE = 32768, MAX_QUAD_INDICES = 16384, MAX_TESS_DIVISIONS_PER_SIDE = 16, // We can put any arbitrary number here, but we should tie it to the max that HW tessellators can do
};
//-----------------------------------------------------------------------------
//
// 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 );
void SetIndexStreamState( int nFirstVertexIdx );
virtual bool IsExternal() const { return false; }
#ifdef CHECK_INDICES
unsigned short GetShadowIndex( int i ) const; #endif
IDirect3DIndexBuffer9 *m_pIndexBuffer;
private: 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
friend class CExternalIndexBufferDx8; };
#ifdef _GAMECONSOLE
#include "tier0/memdbgoff.h"
//-----------------------------------------------------------------------------
// For externally allocated index buffers
//-----------------------------------------------------------------------------
class CExternalIndexBufferDx8 : public CIndexBufferDx8 { typedef CIndexBufferDx8 BaseClass;
public: // constructor
CExternalIndexBufferDx8( ) : BaseClass( SHADER_BUFFER_TYPE_STATIC, MATERIAL_INDEX_FORMAT_16BIT, 0, "external ib - ignore" ) { }
virtual ~CExternalIndexBufferDx8() { if( IsPS3() ) { m_pIndexBuffer = NULL; // we don't have to release the external dynamic IB
} }
void Init( int nIndexCount, uint16 *pIndexData ) { m_pIndexBuffer = CreateExternalDynamicIB( pIndexData, nIndexCount ); m_nBufferSize = m_nFirstUnwrittenOffset = nIndexCount * sizeof(uint16); m_nIndexCount = nIndexCount; }
virtual bool IsExternal() const { return true; } };
#include "tier0/memdbgon.h"
#endif // _GAMECONSOLE
//-----------------------------------------------------------------------------
//
// 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, bool bHasVertexOverride, bool bHasIndexOverride ); virtual VertexFormat_t GetVertexFormat() const;
// Sets/gets the morph format
// Am I using morph data?
bool IsUsingVertexID() const { return ShaderAPI()->GetBoundMaterial()->IsUsingVertexID(); }
virtual bool IsExternal() const { return false; }
// 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 ); }
virtual void GetColorMesh( const IVertexBuffer** pMesh, int *pMeshVertexOffsetInBytes ) const { *pMesh = 0; *pMeshVertexOffsetInBytes = 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; } VertexStreamSpec_t *GetVertexStreamSpec() const { return NULL; } // Draws the mesh
void DrawMesh( const Vector4D *pVecDiffuseModulation );
// 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 int ComputeMemoryUsed();
bool m_bMeshLocked;
protected: bool DebugTrace() const; // The vertex format we're using...
VertexFormat_t m_VertexFormat; #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, MeshBuffersAllocationSettings_t *pSettings = 0 ); 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 ); virtual void GetColorMesh( const IVertexBuffer** pMesh, int *pMeshVertexOffsetInBytes ) const; void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ); void DisableFlexMesh();
virtual void HandleLateCreation( );
bool HasColorMesh( ) const; bool HasFlexMesh( ) const; VertexStreamSpec_t *GetVertexStreamSpec() const; void SetVertexStreamSpec( VertexStreamSpec_t *pStreamSpec );
virtual void * AccessRawHardwareDataStream( uint8 nRawStreamIndex, uint32 numBytes, uint32 uiFlags, void *pvContext );
// Draws the mesh
void Draw( int nFirstIndex, int nIndexCount ); virtual void DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex, int nIndexCount ); void Draw( CPrimList *pLists, int nLists ); void DrawInternal( const Vector4D *pDiffuseModulation, CPrimList *pLists, int nLists );
void DrawPrims( const unsigned char *pInstanceCommandBuffer ); // Draws a single pass
void RenderPass( const unsigned char *pInstanceCommandBuffer ); void RenderPassForInstances( const unsigned char *pInstanceCommandBuffer );
// Sets the primitive type
void SetPrimitiveType( MaterialPrimitiveType_t type ); MaterialPrimitiveType_t GetPrimitiveType() const;
// Is it using tessellation
#if ENABLE_TESSELLATION
TessellationMode_t GetTessellationType() const; #else
TessellationMode_t GetTessellationType() const { return TESSELLATION_MODE_DISABLED; } #endif
// Is it using vertexID for morphs or subdivision surfaces?
bool IsUsingVertexID() const;
bool IsDynamic() const { return false; }
protected: // Sets the render state.
bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, int nIDOffsetBytes = 0, 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, MeshBuffersAllocationSettings_t *pSettings = 0 ); void Unlock( int nIndexCount, IndexDesc_t &desc );
// computes how many primitives we've got
int NumPrimitives( int nVertexCount, int nIndexCount ) const; int NumPrimitives( MaterialPrimitiveType_t type, int nIndexCount ) const;
// Debugging output...
void SpewMaterialVerts( );
// Stream source setting methods
void SetVertexIDStreamState( int nIDOffsetBytes ); void SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel ); void SetColorStreamState_Internal( CVertexBuffer *pColorVB, int nVertOffset ); inline void SetColorStreamState( ); void SetCustomStreamsState( ); void SetVertexStreamState( int nVertOffsetInBytes, bool bIsRenderingInstances ); void SetIndexStreamState( int firstVertexIdx );
void CheckIndices( CPrimList *pPrim, int numPrimitives ); void CheckIndices( int nFirstIndex, 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;
VertexFormat_t m_fmtStreamSpec; CArrayAutoPtr< VertexStreamSpec_t > m_pVertexStreamSpec; CVertexBuffer *m_pVbTexCoord1; LPDIRECT3DVERTEXBUFFER m_arrRawHardwareDataStreams[1];
CVertexBuffer *m_pFlexVertexBuffer;
bool m_bHasRawHardwareDataStreams; 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
friend void CheckIndices( D3DPRIMITIVETYPE nMode, int nFirstVertex, int nVertexCount, int nBaseIndex, int nFirstIndex, int numPrimitives ); };
//-----------------------------------------------------------------------------
// 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, bool bHasVertexOverride, bool bHasIndexOverride );
// 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, MeshBuffersAllocationSettings_t *pSettings = 0 );
// 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 ); virtual void DrawModulated( const Vector4D &diffuseModulation, 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();
void DrawInternal( const Vector4D *pVecDiffuseModulation, int nFirstIndex, int nIndexCount );
// 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; };
#ifdef _GAMECONSOLE
//-----------------------------------------------------------------------------
// For use as a mesh that we've already written into write-combined memory
//-----------------------------------------------------------------------------
class CExternalMeshDX8 : public CMeshDX8 { typedef CMeshDX8 BaseClass;
public: // constructor, destructor
CExternalMeshDX8() : BaseClass( "external vb - ignore" ) { m_pVertexBufferExternal = new CVertexBuffer; m_pIndexBufferExternal = new CIndexBuffer; }
virtual ~CExternalMeshDX8() { CleanUp(); } void CleanUp() { if ( m_pVertexBufferExternal ) { if( m_pVertexBufferExternal == m_pVertexBuffer ) { m_pVertexBuffer = NULL; // let's avoid double-delete
} delete m_pVertexBufferExternal; m_pVertexBufferExternal = NULL; }
if ( m_pIndexBufferExternal ) { if( m_pIndexBufferExternal == m_pIndexBuffer ) { m_pIndexBuffer = NULL; // let's avoid double-delete
} delete m_pIndexBufferExternal; m_pIndexBufferExternal = NULL; } }
// Initializes the mesh
void Init( const ExternalMeshInfo_t& info ) { m_NumVertices = 0; m_NumIndices = 0;
// SetMaterial is only for debugging;
// it actually shows up a tiny bit on the profile might as well ifdef it
#ifdef _DEBUG
SetMaterial( info.m_pMaterial ); #endif
if ( info.m_pVertexOverride ) { CBaseMeshDX8 *pDX8Mesh = static_cast<CBaseMeshDX8*>( info.m_pVertexOverride ); SetVertexFormat( pDX8Mesh->GetVertexFormat(), true, ( info.m_pIndexOverride != NULL ) ); m_pVertexBuffer = pDX8Mesh->GetVertexBuffer(); } else { SetVertexFormat( info.m_VertexFormat, false, ( info.m_pIndexOverride != NULL ) ); m_pVertexBuffer = m_pVertexBufferExternal; }
if ( info.m_pIndexOverride ) { CBaseMeshDX8 *pDX8Mesh = static_cast<CBaseMeshDX8*>( info.m_pIndexOverride ); m_pIndexBuffer = pDX8Mesh->GetIndexBuffer(); } else { m_pIndexBuffer = m_pIndexBufferExternal; } }
void SetExternalData( const ExternalMeshData_t &data ) { if ( m_pVertexBuffer == m_pVertexBufferExternal ) { if ( data.m_nVertexCount > 0 ) { m_pVertexBuffer->Init( Dx9Device(), GetVertexFormat(), 0, data.m_pVertexData, data.m_nVertexSizeInBytes, data.m_nVertexCount ); m_NumVertices = data.m_nVertexCount; } else { m_pVertexBuffer = NULL; m_NumVertices = 0; } }
if ( m_pIndexBuffer == m_pIndexBufferExternal ) { if ( data.m_nIndexCount > 0 ) { m_pIndexBuffer->Init( Dx9Device(), data.m_pIndexData, data.m_nIndexCount ); m_NumIndices = data.m_nIndexCount; } else { m_pIndexBuffer = NULL; m_NumIndices = 0; } } }
virtual bool IsExternal() const { return true; }
private: CVertexBuffer *m_pVertexBufferExternal; CIndexBuffer *m_pIndexBufferExternal; }; #endif // _GAMECONSOLE
//-----------------------------------------------------------------------------
// 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, bool bHasVertexOverride, bool bHasIndexOverride );
// Locks/unlocks the mesh
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc, MeshBuffersAllocationSettings_t *pSettings = 0 ); 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;
// Using tessellation for subd surfaces?
TessellationMode_t GetTessellationType() const { return TESSELLATION_MODE_DISABLED; }
// Begins a pass
void BeginPass( );
virtual void DrawPrims( const unsigned char *pInstanceCommandBuffer ) {}
// Draws a single pass
void RenderPass( const unsigned char *pInstanceCommandBuffer );
virtual void HandleLateCreation() { Assert( !"TBD - CTempMeshDX8::HandleLateCreation()" ); }
// Draws the entire beast
void Draw( int nFirstIndex, int nIndexCount ); virtual void DrawModulated( const Vector4D &diffuseModulation, 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 ); void DrawInternal( const Vector4D *pVecDiffuseModulation, int nFirstIndex, int nIndexCount );
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
//-----------------------------------------------------------------------------
// 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 vertex buffers
void DiscardVertexBuffers();
// Creates, destroys static meshes
IMesh *CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial *pMaterial = NULL, VertexStreamSpec_t *pStreamSpec = 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( ); 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( const unsigned char *pInstanceCommandBuffer );
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 *GetDynamicIndexBufferInternal();
// Is the mesh dynamic?
bool IsDynamicMesh( 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(); CVertexBuffer *GetEmptyColorBuffer();
CIndexBuffer *GetPreTessPatchIndexBuffer( int iSubdivLevel ); CVertexBuffer *GetPreTessPatchVertexBuffer( int iSubdivLevel );
// Helper to determine the number of indices for a particular patch subdiv level
static int GetNumIndicesForSubdivisionLevel( int iSubdivLevel );
IVertexBuffer *GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered = true ); virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ); virtual void DrawInstances( int nInstanceCount, const MeshInstanceData_t *pInstances );
#ifdef _GAMECONSOLE
virtual int GetDynamicIndexBufferAllocationCount(); virtual int GetDynamicIndexBufferIndicesLeft();
// Backdoor used by the queued context to directly use write-combined memory
virtual IMesh *GetExternalMesh( const ExternalMeshInfo_t& info ); virtual void SetExternalMeshData( IMesh *pMesh, const ExternalMeshData_t &data ); virtual IIndexBuffer *GetExternalIndexBuffer( int nIndexCount, uint16 *pIndexData ); #endif
int UnusedVertexFields() const { return m_nUnusedVertexFields; } int UnusedTextureCoords() const { return m_nUnusedTextureCoords; }
void RenderPassForInstances( const unsigned char *pInstanceCommandBuffer ); void DrawInstancedPrims( const unsigned char *pInstanceCommandBuffer );
IDirect3DVertexBuffer9 *GetZeroVertexBuffer() const { return m_pZeroVertexBuffer; }
// Mesh builder used to modify vertex data
CMeshBuilder m_ModifyBuilder;
private: void SetVertexIDStreamState( int nIDOffsetBytes ); void SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel ); void SetColorStreamState( ); void SetCustomStreamsState( ); 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 vertexID buffer
void CreateVertexIDBuffer(); void DestroyVertexIDBuffer();
// Creates, destroys the empty color buffer
void CreateEmptyColorBuffer(); void DestroyEmptyColorBuffer();
void CreateZeroVertexBuffer(); void DestroyZeroVertexBuffer();
// Fills a vertexID buffer
void FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount ); void FillEmptyColorBuffer( CVertexBuffer *pEmptyColorBuffer, int nCount );
// struct for our patch vertices
struct PreTessPatchVertex_t { Vector2D m_vPatchUV; Vector4D m_vBasisU; Vector4D m_vBasisV; };
// Creates, destroys the pre-tessellated patch buffers
void CreatePreTessPatchIndexBuffers(); void CreatePreTessPatchVertexBuffers(); void DestroyPreTessPatchIndexBuffers(); void DestroyPreTessPatchVertexBuffers();
// Fills a pre-tessellated patch IB / VB
void FillPreTessPatchIB( CIndexBuffer* pIndexBuffer, int iSubdivLevel, int nIndexCount ); void FillPreTessPatchVB( CVertexBuffer* pVertexBuffer, int iSubdivLevel, int nVertexCount );
// The dynamic index buffer
CIndexBuffer *m_pDynamicIndexBuffer;
// A static vertexID buffer
CVertexBuffer *m_pVertexIDBuffer;
// Used when we don't have a static color buffer
CVertexBuffer *m_pEmptyColorBuffer;
// Static pre-tessellated patch index/vertex buffers.
CIndexBuffer *m_pPreTessPatchIndexBuffer[ MAX_TESS_DIVISIONS_PER_SIDE ]; CVertexBuffer *m_pPreTessPatchVertexBuffer[ MAX_TESS_DIVISIONS_PER_SIDE ];
// The dynamic vertex buffers
CUtlVector< VertexBufferLookup_t > m_DynamicVertexBuffers;
// 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;
// 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;
#ifdef _GAMECONSOLE
CExternalMeshDX8 m_ExternalMesh; CExternalMeshDX8 m_ExternalFlexMesh; CExternalIndexBufferDx8 m_ExternalIndexBuffer; #endif // _GAMECONSOLE
};
//-----------------------------------------------------------------------------
// 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 CVertexBuffer *g_pLastColorBuffer = NULL; static VertexStreamSpec_t *g_pLastStreamSpec = NULL; static void *g_pLastRawHardwareDataStream = NULL; static bool g_bCustomStreamsSet[ 16 ]; static int g_nLastColorMeshVertOffsetInBytes = 0; static bool g_bUsingVertexID = false; static int g_nLastVertexIDOffset = -1; static bool g_bUsingPreTessPatches = 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 ); }
inline void D3DSetIndices( IDirect3DIndexBuffer9 *pIndexBuffer ) { if ( g_pLastIndexBuffer != pIndexBuffer ) { Dx9Device()->SetIndices( pIndexBuffer ); g_pLastIndexBuffer = pIndexBuffer; } }
//-----------------------------------------------------------------------------
// 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_pLastColorBuffer = 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() { g_pLastIndex = 0; g_pLastIndexBuffer = 0; g_pLastVertex = 0; g_nLastVertOffsetInBytes = 0; g_pLastColorBuffer = 0; g_nLastColorMeshVertOffsetInBytes = 0; g_LastVertexIdx = -1; g_bUsingVertexID = false; g_nLastVertexIDOffset = -1; g_bUsingPreTessPatches = false; g_LastVertexFormat = 0; }
//-----------------------------------------------------------------------------
// Makes sure that the render state is always set next time
//-----------------------------------------------------------------------------
static void ResetIndexBufferRenderState() { g_pLastIndex = 0; 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() { 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; }
D3DPOOL d3dPool = m_bIsDynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; HRESULT hr = Dx9Device()->CreateIndexBuffer( m_nBufferSize, usage, format, d3dPool, &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, &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
#if SHADERAPI_NO_D3DDeviceWrapper
m_pIndexBuffer->Release(); #else
Dx9Device()->Release( m_pIndexBuffer ); #endif
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; }
// Unbind this bad boy if we've currently got it bound
if ( g_pLastIndexBuffer == m_pIndexBuffer ) { Dx9Device()->SetIndices( NULL ); g_pLastIndex = NULL; g_pLastIndexBuffer = NULL; }
// 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 )
#if SHADERAPI_NO_D3DDeviceWrapper
hr = m_pIndexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); #else
hr = Dx9Device()->Lock( m_pIndexBuffer, m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); #endif
#else
hr = m_pIndexBuffer->Lock( 0, 0, &pLockedData, nLockFlags ); pLockedData = ( ( unsigned char * )pLockedData + m_nFirstUnwrittenOffset ); #endif
if ( FAILED( hr ) ) { FailedLock( "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 = (unsigned short*)( g_pScratchIndexBuffer ); 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 ) { #if SHADERAPI_NO_D3DDeviceWrapper
m_pIndexBuffer->Unlock(); #else
Dx9Device()->Unlock( m_pIndexBuffer ); #endif
}
m_nFirstUnwrittenOffset += nWrittenIndexCount * IndexSize(); m_bIsLocked = false; g_ShaderMutex.Unlock(); }
void CIndexBufferDx8::SetIndexStreamState( int firstVertexIdx ) { if ( g_pLastIndex || g_pLastIndexBuffer != m_pIndexBuffer || ( IsGameConsole() && ( IsDynamic() || IsExternal() ) ) ) { D3DSetIndices( m_pIndexBuffer ); HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
g_pLastIndex = NULL; g_LastVertexIdx = -1; } }
//-----------------------------------------------------------------------------
//
// 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; DWORD usage = D3DUSAGE_WRITEONLY; if ( m_bIsDynamic ) { usage |= D3DUSAGE_DYNAMIC; pool = D3DPOOL_DEFAULT;
//
// UNDONE: Don't call this since GetVertexFormat will assert if m_bIsDynamic is set, and
// dynamic meshes shouldn't really have a fixed vertex format anyway.
//
// 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, m_bIsDynamic ? 0 : VertexSize(), m_VertexFormat );
#ifdef VPROF_ENABLED
if ( IsGameConsole() || !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 ( IsGameConsole() || !m_bIsDynamic ) { Assert( m_pGlobalCounter ); *m_pGlobalCounter -= m_nBufferSize; } #endif
#if SHADERAPI_NO_D3DDeviceWrapper
m_pVertexBuffer->Release(); #else
Dx9Device()->Release( m_pVertexBuffer ); #endif
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 )
#if SHADERAPI_NO_D3DDeviceWrapper
hr = m_pVertexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); #else
hr = Dx9Device()->Lock( m_pVertexBuffer, m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); #endif
#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 ) ) ) { FailedLock( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com\n" ); } else { FailedLock( "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 ) { #if SHADERAPI_NO_D3DDeviceWrapper
m_pVertexBuffer->Unlock(); #else
Dx9Device()->Unlock( m_pVertexBuffer ); #endif
}
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 DBGFLAG_ASSERT
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, bool bHasVertexOverride, bool bHasIndexOverride ) { m_VertexFormat = format; }
VertexFormat_t CBaseMeshDX8::GetVertexFormat() const { return m_VertexFormat; }
//-----------------------------------------------------------------------------
// 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 int CBaseMeshDX8::ComputeMemoryUsed() { unsigned size = 0;
if ( GetVertexBuffer() ) { size += GetVertexBuffer()->AllocationSize(); }
if ( GetIndexBuffer() ) { size += GetIndexBuffer()->AllocationSize(); }
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( const Vector4D *pVecDiffuseModulation ) { #ifdef DBGFLAG_ASSERT
// Make sure we're not drawing...
Assert( !m_IsDrawing ); m_IsDrawing = true; #endif
Assert( !g_pInstanceData );
MeshInstanceData_t instance; CompiledLightingState_t *pCompiledLightingState; InstanceInfo_t *pInstanceInfo; TessellationMode_t nTessellationMode = TESSELLATION_MODE_DISABLED;
ShaderAPI()->GenerateNonInstanceRenderState( &instance, &pCompiledLightingState, &pInstanceInfo ); GetColorMesh( &instance.m_pColorBuffer, &instance.m_nColorVertexOffsetInBytes ); if ( pVecDiffuseModulation ) { instance.m_DiffuseModulation = *pVecDiffuseModulation;
Vector4D matModulation; IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial(); pMaterial->GetColorModulation( &matModulation[0], &matModulation[1], &matModulation[2] ); matModulation[3] = pMaterial->GetAlphaModulation();
instance.m_DiffuseModulation *= matModulation; } else { IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial(); pMaterial->GetColorModulation( &instance.m_DiffuseModulation[0], &instance.m_DiffuseModulation[1], &instance.m_DiffuseModulation[2] ); instance.m_DiffuseModulation[3] = pMaterial->GetAlphaModulation(); }
ShaderAPI()->SetTessellationMode( GetTessellationType() );
// This is going to cause RenderPass to get called a bunch
ShaderAPI()->DrawMesh( this, 1, &instance, CompressionType( GetVertexFormat() ), pCompiledLightingState, pInstanceInfo );
if ( nTessellationMode != TESSELLATION_MODE_DISABLED ) { ShaderAPI()->SetTessellationMode( TESSELLATION_MODE_DISABLED ); }
#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 DBGFLAG_ASSERT
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( uint( pIndex[0] ) < 16 ); Assert( uint( pIndex[1] ) < 16 ); Assert( uint( pIndex[2] ) < 16 ); Assert( uint( 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;
#if ( PLATFORM_WINDOWS_PC || ( defined( _X360 ) ) )
#define PLATFORM_SUPPORTS_TRIANGLE_FANS 1
#else
#define PLATFORM_SUPPORTS_TRIANGLE_FANS 0
#endif
//-----------------------------------------------------------------------------
// 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_fmtStreamSpec( 0 ), m_pVbTexCoord1( 0 ), m_pIndexBuffer(0), m_Type(MATERIAL_TRIANGLES), m_IsVBLocked(false), m_IsIBLocked(false) { V_memset( m_arrRawHardwareDataStreams, 0, sizeof( m_arrRawHardwareDataStreams ) ); m_bHasRawHardwareDataStreams = false;
m_pTextureGroupName = pTextureGroupName; m_Mode = ComputeMode(m_Type);
m_flexVertCount = 0; m_bHasFlexVerts = false; m_pFlexVertexBuffer = NULL; m_nFlexVertOffsetInBytes = 0; }
CMeshDX8::~CMeshDX8() { // Don't release the vertex buffer
if (!g_MeshMgr.IsDynamicMesh(this)) { delete m_pVbTexCoord1; delete m_pVertexBuffer; delete m_pIndexBuffer;
for ( int k = 0; k < ARRAYSIZE( m_arrRawHardwareDataStreams ); ++ k ) { if ( m_arrRawHardwareDataStreams[k] ) { m_arrRawHardwareDataStreams[k]->Release(); m_arrRawHardwareDataStreams[k] = NULL; } } m_bHasRawHardwareDataStreams = false; } }
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); }
void CMeshDX8::GetColorMesh( const IVertexBuffer** pMesh, int *pMeshVertexOffsetInBytes ) const { *pMesh = m_pColorMesh; *pMeshVertexOffsetInBytes = m_nColorMeshVertOffsetInBytes; }
VertexStreamSpec_t *CMeshDX8::GetVertexStreamSpec() const { return m_pVertexStreamSpec.Get(); }
void CMeshDX8::SetVertexStreamSpec( VertexStreamSpec_t *pStreamSpec ) { m_pVertexStreamSpec.Delete(); m_fmtStreamSpec = 0;
int numSpecs = 0; for ( VertexStreamSpec_t *pCount = pStreamSpec; pCount && pCount->iVertexDataElement != VERTEX_FORMAT_UNKNOWN; ++ pCount, ++ numSpecs ) { if ( pCount->iStreamSpec != VertexStreamSpec_t::STREAM_DEFAULT ) m_fmtStreamSpec |= pCount->iVertexDataElement; }
if ( !numSpecs ) return;
m_pVertexStreamSpec.Attach( new VertexStreamSpec_t[ numSpecs + 1 ] ); memcpy( m_pVertexStreamSpec.Get(), pStreamSpec, (numSpecs + 1)*sizeof( VertexStreamSpec_t ) ); }
//-----------------------------------------------------------------------------
// 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_fmtStreamSpec ); m_pVertexBuffer = new CVertexBuffer( Dx9Device(), m_VertexFormat &~ m_fmtStreamSpec, 0, size, nVertexCount, m_pTextureGroupName, ShaderAPI()->UsingSoftwareVertexProcessing() ); if ( !m_pVertexBuffer ) { MemOutOfMemory( sizeof(CVertexBuffer) ); }
if ( VertexStreamSpec_t *pTexCoord1 = FindVertexStreamSpec( VERTEX_TEXCOORD_SIZE( 1, 2 ), m_pVertexStreamSpec.Get() ) ) { // TODO: actually create a full stream and allow modifications by the clients
DWORD dwVertexFormat = VERTEX_TEXCOORD_SIZE( 1, 2 ); int iVertexSize = 2 * sizeof( float ); int numVbEntries = 1; m_pVbTexCoord1 = new CVertexBuffer( Dx9Device(), dwVertexFormat, 0, iVertexSize, numVbEntries, m_pTextureGroupName, ShaderAPI()->UsingSoftwareVertexProcessing() ); if ( !m_pVbTexCoord1 ) { MemOutOfMemory( sizeof(CVertexBuffer) ); }
} }
// 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 ) { // For debugging: when we get a dump crash of this, we'll know how many vertices were allocated
volatile int nVertexBuffer_VertexCount = m_pVertexBuffer->VertexCount(), nVertexBuffer_VertexSize = m_pVertexBuffer->VertexSize(); NOTE_UNUSED( nVertexBuffer_VertexCount ); NOTE_UNUSED( nVertexBuffer_VertexSize ); 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 ) ) ) { FailedLock( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com\n" ); } else { Assert( 0 ); FailedLock( "failed to lock vertex buffer in CMeshDX8::LockVertexBuffer\n" ); } } CVertexBufferBase::ComputeVertexDescription( 0, 0, desc ); return false; }
// Set up the vertex descriptor
CVertexBufferBase::ComputeVertexDescription( pVertexMemory, m_VertexFormat &~ m_fmtStreamSpec, 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 &~ m_fmtStreamSpec ); 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, MeshBuffersAllocationSettings_t *pSettings ) { 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 = (unsigned short*)( g_pScratchIndexBuffer ); desc.m_nIndexSize = 0; return 0; }
// Static vertex buffer case
if (!m_pIndexBuffer) { m_pIndexBuffer = new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing(), false, pSettings ); }
desc.m_pIndices = m_pIndexBuffer->Lock( bReadOnly, nIndexCount, *(int*)( &desc.m_nFirstIndex ), nFirstIndex ); if( !desc.m_pIndices ) { desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer ); desc.m_nIndexSize = 0; desc.m_nFirstIndex = 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 ) ) ) { FailedLock( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com\n" ); } else { Assert( 0 ); FailedLock( "failed to lock index buffer in CMeshDX8::LockIndexBuffer\n" ); }
return 0; } desc.m_nIndexSize = 1; desc.m_nOffset = 0; m_IsIBLocked = true;
#if defined( RECORDING ) || defined( CHECK_INDICES )
m_LockIndexBufferSize = nIndexCount * 2; m_LockIndexBuffer = desc.m_pIndices; #endif
return desc.m_nFirstIndex; }
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, MeshBuffersAllocationSettings_t *pSettings ) { 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 ), pSettings ); } else { desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer ); 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 = (unsigned short*)( g_pScratchIndexBuffer ); 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 &~ m_fmtStreamSpec, 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 ) { 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; }
#if ENABLE_TESSELLATION
TessellationMode_t CMeshDX8::GetTessellationType() const { switch( GetPrimitiveType() ) { case MATERIAL_SUBD_QUADS_EXTRA: return TESSELLATION_MODE_ACC_PATCHES_EXTRA; case MATERIAL_SUBD_QUADS_REG: return TESSELLATION_MODE_ACC_PATCHES_REG; }
return TESSELLATION_MODE_DISABLED; } #endif
bool CMeshDX8::IsUsingVertexID() const { return ( g_pHardwareConfig->ActualHasFastVertexTextures() && ShaderAPI()->GetBoundMaterial()->IsUsingVertexID() && ( GetTessellationType() > 0 || ( !m_pVertexBuffer->IsDynamic() && !m_pVertexBuffer->IsExternal() ) ) ); }
//-----------------------------------------------------------------------------
// 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;
#ifndef DX_TO_GL_ABSTRACTION
case D3DPT_LINESTRIP: return nIndexCount - 1; #endif
case D3DPT_TRIANGLELIST: return nIndexCount / 3;
case D3DPT_TRIANGLESTRIP: return nIndexCount - 2;
#ifndef DX_TO_GL_ABSTRACTION
case D3DPT_TRIANGLEFAN: // We never use this anywhere else, so we override it to indicate quads
return nIndexCount / 4; #endif
default: // invalid, baby!
Assert(0); }
return 0; }
static int NumPrimitives( MaterialPrimitiveType_t type, int nIndexCount ) { switch( type ) { case MATERIAL_LINES: return nIndexCount / 2;
case MATERIAL_TRIANGLES: return nIndexCount / 3;
case MATERIAL_TRIANGLE_STRIP: return nIndexCount - 2;
case MATERIAL_SUBD_QUADS_EXTRA: case MATERIAL_SUBD_QUADS_REG: return nIndexCount / 4;
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|" ); } 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
static bool IsValidVertexFormat_Internal( VertexFormat_t meshFormat, IMaterial* pMaterial, VertexFormat_t materialFormat ) { // the material format should match the vertex usage, unless another format is passed in
if ( materialFormat == VERTEX_FORMAT_INVALID ) { Assert( pMaterial ); materialFormat = static_cast<IMaterialInternal*>( pMaterial )->GetVertexUsage() & ~( VERTEX_COLOR_STREAM_1 | VERTEX_FORMAT_USE_EXACT_FORMAT );
// Blat out unused fields
materialFormat &= ~g_MeshMgr.UnusedVertexFields(); int nUnusedTextureCoords = g_MeshMgr.UnusedTextureCoords(); for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) { if ( nUnusedTextureCoords & ( 1 << i ) ) { materialFormat &= ~VERTEX_TEXCOORD_MASK( i ); } } } else { materialFormat &= ~( VERTEX_COLOR_STREAM_1 | VERTEX_FORMAT_USE_EXACT_FORMAT ); }
bool bIsValid = (( VERTEX_FORMAT_FIELD_MASK & materialFormat ) & ( VERTEX_FORMAT_FIELD_MASK & ~meshFormat )) == 0;
if ( meshFormat & VERTEX_FORMAT_COMPRESSED ) { // We shouldn't get compressed verts if this material doesn't support them!
if ( ( materialFormat & 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( meshFormat ) >= UserDataSize( materialFormat );
for ( int i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) { if ( TexCoordSize( i, meshFormat ) < TexCoordSize( i, materialFormat ) ) { 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( meshFormat ); bIsValid = bIsValid && ( nWeightCount >= ( g_pShaderAPI->GetCurrentNumBones() - 1 ) );
#ifdef _DEBUG
if ( !bIsValid ) { Warning( "Material Format:" ); if ( g_pShaderAPI->GetCurrentNumBones() > 0 ) { materialFormat |= VERTEX_BONE_INDEX; materialFormat &= ~VERTEX_BONE_WEIGHT_MASK; materialFormat |= VERTEX_BONEWEIGHT( 2 ); }
OutputVertexFormat( materialFormat ); Warning( "Mesh Format:" ); OutputVertexFormat( meshFormat ); } #endif
return bIsValid; }
static inline bool IsValidVertexFormat( VertexFormat_t meshFormat, IMaterial* pMaterial, VertexFormat_t materialFormat = VERTEX_FORMAT_INVALID ) { // 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 = ( meshFormat & VERTEX_FORMAT_COMPRESSED ) && ( ( materialFormat == VERTEX_FORMAT_INVALID ) || ( ( materialFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) );
if ( !bCheckCompression && !IsPC() && !IsDebug() ) return true; return IsValidVertexFormat_Internal( meshFormat, pMaterial, materialFormat ); }
//-----------------------------------------------------------------------------
// Stream source setting methods
//-----------------------------------------------------------------------------
void CMeshDX8::SetVertexIDStreamState( int nIDOffsetBytes ) { // FIXME: this method duplicates the code in CMeshMgr::SetVertexIDStreamState
if ( IsGameConsole() ) return;
bool bUsingVertexID = IsUsingVertexID(); if ( bUsingVertexID != g_bUsingVertexID || g_nLastVertexIDOffset != nIDOffsetBytes ) { 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
// NOTE: SubDivivison surfaces are now using vertex id for the instanced patch case.
// These are dynamic buffers, so now we've bifurcated the VertexID code for dynamic buffers.
Assert( !g_pShaderAPI->IsHWMorphingEnabled() || !m_pVertexBuffer->IsDynamic() || GetTessellationType() > 0 );
CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( ); RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( pVertexIDBuffer->UID() ); RECORD_INT( VertexStreamSpec_t::STREAM_MORPH ); RECORD_INT( nIDOffsetBytes ); RECORD_INT( pVertexIDBuffer->VertexSize() );
D3DSetStreamSource( VertexStreamSpec_t::STREAM_MORPH, pVertexIDBuffer->GetInterface(), nIDOffsetBytes, pVertexIDBuffer->VertexSize() ); pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); } else { RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( -1 ); // vertex buffer id
RECORD_INT( VertexStreamSpec_t::STREAM_MORPH ); // stream
RECORD_INT( 0 ); // vertex offset
RECORD_INT( 0 ); // vertex size
D3DSetStreamSource( VertexStreamSpec_t::STREAM_MORPH, 0, 0, 0 ); } g_bUsingVertexID = bUsingVertexID; g_nLastVertexIDOffset = nIDOffsetBytes; } }
void CMeshDX8::SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel ) { // NOTE: do we need this method in CMeshMgr::SetTessellationStreamState
if ( IsGameConsole() ) return;
bool bUsingPreTessPatches = ( GetTessellationType() > 0 ); if ( bUsingPreTessPatches != g_bUsingPreTessPatches ) { if ( bUsingPreTessPatches ) { // Patches for subdivision start at 1 ( 1 is 0 subdivisions )
iSubdivLevel --;
// Bind our patches VB to stream 0
CVertexBuffer *pPatchVB = g_MeshMgr.GetPreTessPatchVertexBuffer( iSubdivLevel ); RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( pPatchVB->UID() ); RECORD_INT( 0 ); RECORD_INT( 0 ); RECORD_INT( pPatchVB->VertexSize() ); D3DSetStreamSource( 0, pPatchVB->GetInterface(), 0, pPatchVB->VertexSize() ); pPatchVB->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); g_pLastVertex = pPatchVB; g_nLastVertOffsetInBytes = 0;
// Override the index buffer with our patch index buffer
CIndexBuffer* pPatchIB = g_MeshMgr.GetPreTessPatchIndexBuffer( iSubdivLevel ); RECORD_COMMAND( DX8_SET_INDICES, 2 ); RECORD_INT( pPatchIB->UID() ); RECORD_INT( 0 ); Dx9Device()->SetIndices( pPatchIB->GetInterface() ); pPatchIB->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
g_pLastIndex = pPatchIB; g_pLastIndexBuffer = NULL; g_LastVertexIdx = -1; } else { RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( -1 ); // vertex buffer id
RECORD_INT( VertexStreamSpec_t::STREAM_SUBDQUADS ); // stream
RECORD_INT( 0 ); // vertex offset
RECORD_INT( 0 ); // vertex size
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SUBDQUADS, 0, 0, 0 ); } g_bUsingPreTessPatches = bUsingPreTessPatches; } }
void CMeshDX8::SetCustomStreamsState() { if ( ( !g_pLastRawHardwareDataStream && !m_bHasRawHardwareDataStreams ) || ( g_pLastRawHardwareDataStream == m_arrRawHardwareDataStreams ) ) { // Case 1: No old streams set and this mesh has no hw data streams
// Case 2: Old streams set to the same streams that this mesh has
// Nothing to do here
} else { LPDIRECT3DVERTEXBUFFER *arrRawStreams = m_bHasRawHardwareDataStreams ? m_arrRawHardwareDataStreams : NULL; g_pLastRawHardwareDataStream = arrRawStreams; #ifdef _PS3
Dx9Device()->SetRawHardwareDataStreams( arrRawStreams ); #endif
}
if ( m_pVertexStreamSpec.Get() != g_pLastStreamSpec ) { if ( m_pVbTexCoord1 ) { VertexStreamSpec_t *pTexCoord1 = FindVertexStreamSpec( VERTEX_TEXCOORD_SIZE( 1, 2 ), m_pVertexStreamSpec.Get() ); VertexStreamSpec_t::StreamSpec_t iStream = pTexCoord1 ? pTexCoord1->iStreamSpec : VertexStreamSpec_t::STREAM_UNIQUE_A;
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( m_pVbTexCoord1->UID() ); RECORD_INT( iStream ); RECORD_INT( 0 ); RECORD_INT( 0 );
D3DSetStreamSource( iStream, m_pVbTexCoord1->GetInterface(), 0, 0 ); g_bCustomStreamsSet[ iStream ] = true;
m_pVbTexCoord1->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); } else { VertexStreamSpec_t *pTexCoord1 = FindVertexStreamSpec( VERTEX_TEXCOORD_SIZE( 1, 2 ), m_pVertexStreamSpec.Get() ); VertexStreamSpec_t::StreamSpec_t iStream = pTexCoord1 ? pTexCoord1->iStreamSpec : VertexStreamSpec_t::STREAM_UNIQUE_A;
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( -1 ); // vertex buffer id
RECORD_INT( iStream ); // stream
RECORD_INT( 0 ); // vertex offset
RECORD_INT( 0 ); // vertex size
D3DSetStreamSource( iStream, 0, 0, 0 ); g_bCustomStreamsSet[ iStream ] = false; } g_pLastStreamSpec = m_pVertexStreamSpec.Get(); } }
void *CMeshDX8::AccessRawHardwareDataStream( uint8 nRawStreamIndex, uint32 numBytes, uint32 uiFlags, void *pvContext ) { #ifdef _PS3
if ( nRawStreamIndex < ARRAYSIZE( m_arrRawHardwareDataStreams ) ) { if ( !m_arrRawHardwareDataStreams[nRawStreamIndex] ) { Dx9Device()->CreateVertexBuffer( numBytes, uiFlags, 0, D3DPOOL_MANAGED, &m_arrRawHardwareDataStreams[nRawStreamIndex], NULL ); if ( m_arrRawHardwareDataStreams[nRawStreamIndex] ) { void *pbData = NULL; m_arrRawHardwareDataStreams[nRawStreamIndex]->Lock( 0, numBytes, &pbData, D3DLOCK_NOOVERWRITE ); m_bHasRawHardwareDataStreams = true; return pbData; } } else if ( !numBytes && pvContext ) { m_arrRawHardwareDataStreams[nRawStreamIndex]->Unlock(); return NULL; } } Error( "<vitaliy> CMeshDX8::AccessRawHardwareDataStream unsupported codepath!\n" ); #endif
return NULL; }
inline void CMeshDX8::SetColorStreamState( ) { CVertexBuffer *pColorVB = m_pColorMesh ? m_pColorMesh->GetVertexBuffer() : g_MeshMgr.GetEmptyColorBuffer(); int nVertOffset = m_pColorMesh ? m_nColorMeshVertOffsetInBytes : 0; if ( ( pColorVB != g_pLastColorBuffer ) || ( nVertOffset != g_nLastColorMeshVertOffsetInBytes ) ) { SetColorStreamState_Internal( pColorVB, nVertOffset ); } }
void CMeshDX8::SetColorStreamState_Internal( CVertexBuffer *pColorVB, int nVertOffset ) { RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( pColorVB->UID() ); RECORD_INT( VertexStreamSpec_t::STREAM_SPECULAR1 ); RECORD_INT( nVertOffset ); RECORD_INT( pColorVB->VertexSize() );
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SPECULAR1, pColorVB->GetInterface(), nVertOffset, pColorVB->VertexSize() ); pColorVB->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
g_pLastColorBuffer = pColorVB; g_nLastColorMeshVertOffsetInBytes = nVertOffset; }
void CMeshDX8::SetVertexStreamState( int nVertOffsetInBytes, bool bIsRenderingInstances ) { bool bUsingPreTessPatches = ( GetTessellationType() > 0 );
// Calls in here assume shader support...
if ( !bIsRenderingInstances && HasFlexMesh() ) { // m_pFlexVertexBuffer is the flex buffer down inside the CMeshMgr singleton
D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, m_pFlexVertexBuffer->GetInterface(), m_nFlexVertOffsetInBytes, m_pFlexVertexBuffer->VertexSize() );
// cFlexScale.x masks flex in vertex shader
float c[4] = { 1.0f, g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 92 ? 1.0f : 0.0f, 0.0f, 0.0f }; ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); } else if ( bUsingPreTessPatches ) { // Override the original vertex buffer because we cannot have instance data in stream 0
Assert( m_pVertexBuffer ); RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( m_pVertexBuffer->UID() ); RECORD_INT( VertexStreamSpec_t::STREAM_SUBDQUADS ); RECORD_INT( nVertOffsetInBytes ); RECORD_INT( m_pVertexBuffer->VertexSize() * 4 ); D3DSetStreamSource( VertexStreamSpec_t::STREAM_SUBDQUADS, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() * 4 ); m_pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
g_pLastVertex = NULL; g_nLastVertOffsetInBytes = -1; } else if( GetTessellationType() == 0 ) { 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! (Supressing furthur warnings)\n", minimumStreamZeroStride ); bWarned = true; } } // 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)
D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 ); //D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() );
// cFlexScale.x masks flex in vertex shader
if ( !bIsRenderingInstances ) { float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); } }
// MESHFIXME: Make sure this jives between the mesh/ib/vb version.
if( !bUsingPreTessPatches ) { // [will] - Added defined( OSX ) because Scaleform renderer circumvents the MeshMgr and changes internal vertex buffer, so we can't rely on caching it.
#if defined( _GAMECONSOLE ) || defined( OSX )
if ( ( g_pLastVertex != m_pVertexBuffer ) || m_pVertexBuffer->IsDynamic() || m_pVertexBuffer->IsExternal() || ( 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; } }
if ( ( !g_pLastRawHardwareDataStream && !m_bHasRawHardwareDataStreams ) || ( g_pLastRawHardwareDataStream == m_arrRawHardwareDataStreams ) ) { // Case 1: No old streams set and this mesh has no hw data streams
// Case 2: Old streams set to the same streams that this mesh has
// Nothing to do here
} else { LPDIRECT3DVERTEXBUFFER *arrRawStreams = m_bHasRawHardwareDataStreams ? m_arrRawHardwareDataStreams : NULL; g_pLastRawHardwareDataStream = arrRawStreams; #ifdef _PS3
Dx9Device()->SetRawHardwareDataStreams( arrRawStreams ); #endif
} }
void CMeshDX8::SetIndexStreamState( int firstVertexIdx ) { if( !( GetTessellationType() > 0 ) ) { #ifdef _GAMECONSOLE
if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || m_pIndexBuffer->IsDynamic() || m_pIndexBuffer->IsExternal() || ( 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 );
D3DSetIndices( m_pIndexBuffer->GetInterface() ); m_pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); m_FirstIndex = firstVertexIdx;
g_pLastIndex = m_pIndexBuffer; g_LastVertexIdx = firstVertexIdx; } } }
static ConVar mat_tessellationlevel( "mat_tessellationlevel", "6", FCVAR_CHEAT );
bool CMeshDX8::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, int nIDOffsetBytes, VertexFormat_t vertexFormat ) { // Can't set the state if we're deactivated
if ( g_pShaderDeviceDx8->IsDeactivated() ) { ResetMeshRenderState(); return false; }
g_LastVertexFormat = vertexFormat; int iSubdivLevel = (int)ceil( mat_tessellationlevel.GetFloat() );
if ( iSubdivLevel > MAX_TESS_DIVISIONS_PER_SIDE ) { mat_tessellationlevel.SetValue( MAX_TESS_DIVISIONS_PER_SIDE ); }
if ( iSubdivLevel < 1 ) { mat_tessellationlevel.SetValue( 1 ); }
iSubdivLevel = MIN( MAX_TESS_DIVISIONS_PER_SIDE, MAX( 1, iSubdivLevel ) );
SetVertexIDStreamState( nIDOffsetBytes ); SetColorStreamState(); SetCustomStreamsState(); SetVertexStreamState( nVertexOffsetInBytes, false ); SetIndexStreamState( nFirstVertexIdx ); SetTessellationStreamState( nVertexOffsetInBytes, iSubdivLevel );
return true; }
//-----------------------------------------------------------------------------
// Draws the static mesh
//-----------------------------------------------------------------------------
void CMeshDX8::DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex, int nIndexCount ) { if ( !ShaderUtil()->OnDrawMeshModulated( this, diffuseModulation, 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( &diffuseModulation, &primList, 1 ); }
void CMeshDX8::Draw( int nFirstIndex, int nIndexCount ) { 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( NULL, &primList, 1 ); }
void CMeshDX8::Draw( CPrimList *pLists, int nLists ) { if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) ) { MarkAsDrawn(); return; }
DrawInternal( NULL, pLists, nLists ); }
void CMeshDX8::DrawInternal( const Vector4D *pDiffuseModulation, CPrimList *pLists, int nLists ) { #ifdef DX_TO_GL_ABSTRACTION
HandleLateCreation(); #endif
// 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( pDiffuseModulation ); }
#ifdef CHECK_INDICES
void CheckIndices( D3DPRIMITIVETYPE nMode, int nFirstVertex, int nVertexCount, int nBaseIndex, int nFirstIndex, int numPrimitives ) { // g_pLastVertex - this is the current vertex buffer
// g_pLastColorBuffer - this is the current color mesh, if there is one.
// g_pLastIndex - this is the current index buffer.
// vertoffset : m_FirstIndex
// NOTE: This doesn't work for pure index buffers yet
if ( !g_pLastIndex || !g_pLastVertex ) return;
if( nMode == D3DPT_TRIANGLELIST || nMode == D3DPT_TRIANGLESTRIP ) { Assert( nFirstIndex >= 0 && nFirstIndex < g_pLastIndex->IndexCount() ); int i; for( i = 0; i < 2; i++ ) { CVertexBuffer *pMesh; if( i == 0 ) { pMesh = g_pLastVertex; Assert( pMesh ); } else { if( !g_pLastColorBuffer || g_pLastColorBuffer == g_MeshMgr.GetEmptyColorBuffer() ) continue; pMesh = g_pLastColorBuffer; if( !pMesh ) continue; } Assert( nFirstVertex >= 0 && (int)( nFirstVertex + nBaseIndex ) < pMesh->VertexCount() ); int nIndexCount = 0; if( nMode == D3DPT_TRIANGLELIST ) { nIndexCount = numPrimitives * 3; } else if( nMode == D3DPT_TRIANGLESTRIP ) { nIndexCount = numPrimitives + 2; } else { Assert( 0 ); } int j; for( j = 0; j < nIndexCount; j++ ) { int index = g_pLastIndex->GetShadowIndex( j + nFirstIndex ); Assert( index >= (int)nFirstVertex ); Assert( index < (int)(nFirstVertex + nVertexCount) ); } } } }
void CMeshDX8::CheckIndices( int nFirstIndex, int numPrimitives ) { ::CheckIndices( m_Mode, s_FirstVertex, s_NumVertices, m_FirstIndex, nFirstIndex, numPrimitives ); }
void CMeshDX8::CheckIndices( CPrimList *pPrim, int numPrimitives ) { CheckIndices( pPrim->m_FirstIndex, numPrimitives ); } #endif // CHECK_INDICES
void CMeshDX8::DrawPrims( const unsigned char *pInstanceCommandBuffer ) { // Set up the "per-instance" render state for non-instanced draw calls
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, 0, false );
for ( int iPrim=0; iPrim < s_nPrims; iPrim++ ) { CPrimList *pPrim = &s_pPrims[iPrim];
if ( pPrim->m_NumIndices == 0 ) continue;
int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices );
{ 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 ); } } }
void CMeshDX8::RenderPass( const unsigned char *pInstanceCommandBuffer ) { LOCK_SHADERAPI(); VPROF( "CMeshDX8::RenderPass" );
#ifdef DX_TO_GL_ABSTRACTION
HandleLateCreation(); #endif
if ( g_nInstanceCount ) { g_MeshMgr.RenderPassForInstances( pInstanceCommandBuffer ); return; }
Assert( m_Type != MATERIAL_HETEROGENOUS );
// JasonM - skip this validation for subd quads
if ( m_Type != MATERIAL_SUBD_QUADS_EXTRA && m_Type != MATERIAL_SUBD_QUADS_REG ) { // make sure the vertex format is a superset of the current material's vertex format...
if ( !IsValidVertexFormat( m_VertexFormat, ShaderAPI()->GetBoundMaterial(), 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; } }
// Set up the "per-instance" render state for non-instanced draw calls
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, 0, false );
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 ) ) { // (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 if ( m_Type == MATERIAL_SUBD_QUADS_EXTRA || m_Type == MATERIAL_SUBD_QUADS_REG ) { //#if ( defined ( _X360 ) || defined ( DX_TO_GL_ABSTRACTION ) )
#if ( 1 )
AssertMsg( false, "MATERIAL_SUBD_QUADS are not supported" ); #else
Assert( ShaderAPI()->GetTessellationMode() != TESSELLATION_MODE_DISABLED );
Dx9Device()->SetTessellationLevel( MIN( MAX_TESS_DIVISIONS_PER_SIDE, MAX( 1, mat_tessellationlevel.GetFloat() ) ) ); Dx9Device()->DrawTessellatedIndexedPrimitive( m_FirstIndex, s_FirstVertex, s_NumVertices, pPrim->m_FirstIndex, pPrim->m_NumIndices / 4 ); #endif
} 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 );
#if defined( _X360 )
IDirect3DVertexShader9 *pVertShader = NULL; Dx9Device()->GetVertexShader( &pVertShader ); if ( pVertShader != NULL ) { pVertShader->Release(); // NOTE: IDirect3DDevice9::GetVertexShader increments the shader's internal refcount!
#endif // _X360
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 accessing 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 defined( _X360 )
} else { Warning( "CMeshDX8::RenderPass - Material \"%s\" has no vertex shader applied!\n", ShaderAPI()->GetBoundMaterial()->GetName() ); } #endif // _X360
} } }
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; m_pIndexBuffer = 0; 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, bool bHasVertexOverride, bool bHasIndexOverride ) { 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; }
format &= ~VERTEX_COLOR_STREAM_1;
if ((format != m_VertexFormat) || m_VertexOverride || m_IndexOverride) { m_VertexFormat = format;
if ( !bHasVertexOverride ) { UseVertexBuffer( g_MeshMgr.FindOrCreateVertexBuffer( m_nBufferId, format ) ); m_VertexOverride = false; }
if ( m_nBufferId == 0 && !bHasIndexOverride ) { UseIndexBuffer( g_MeshMgr.GetDynamicIndexBufferInternal() ); 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 { Assert( m_pVertexBuffer != NULL );
if ( g_pShaderDeviceDx8->IsDeactivated() ) return false;
// 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, MeshBuffersAllocationSettings_t *pSettings ) { ShaderUtil()->SyncMatrices();
g_ShaderMutex.Lock();
// Yes, this may well also be called from BufferedMesh but that's ok
PreLock();
if (m_VertexOverride) { nVertexCount = 0; }
if (m_IndexOverride) { nIndexCount = 0; }
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 && m_Type != MATERIAL_INSTANCED_QUADS ) { int nFirstIndex = Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ), pSettings ); if (m_FirstIndex < 0) { m_FirstIndex = nFirstIndex; } } else { desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer ); desc.m_nIndexSize = 0; desc.m_nFirstIndex = 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::DrawInternal( const Vector4D *pVecDiffuseModulation, 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 ); #ifdef DX_TO_GL_ABSTRACTION
HandleLateCreation(); #endif
// 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; bool bUsingPreTessellatedPatches = ( GetTessellationType() > 0 ); int nVertexOffsetInBytes = 0; int nIDOffsetBytes = 0; if ( bUsingPreTessellatedPatches ) { CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( );
nVertexOffsetInBytes = ( nFirstVertex + ( nFirstIndex ) ) * g_MeshMgr.VertexFormatSize( GetVertexFormat() ); nIDOffsetBytes = ( nFirstIndex / 4 ) * pVertexIDBuffer->VertexSize(); } else if ( HasFlexMesh() ) { nVertexOffsetInBytes = nFirstVertex * g_MeshMgr.VertexFormatSize( GetVertexFormat() ); } int baseIndex = m_IndexOverride ? 0 : m_FirstIndex;
// Overriding with the dynamic index buffer, preserve state!
if ( m_IndexOverride && m_pIndexBuffer == g_MeshMgr.GetDynamicIndexBufferInternal() ) { baseIndex = m_FirstIndex; }
VertexFormat_t fmt = m_VertexOverride ? GetVertexFormat() : VERTEX_FORMAT_INVALID; if ( !SetRenderState( nVertexOffsetInBytes, actualFirstVertex, nIDOffsetBytes, fmt ) ) return;
// Draws a portion of the mesh
int numVertices = m_VertexOverride ? m_pVertexBuffer->VertexCount() : m_TotalVertices; if ((nFirstIndex != -1) && (nIndexCount != 0)) { Assert( ( m_Type != MATERIAL_POINTS ) && ( m_Type != MATERIAL_INSTANCED_QUADS ) ); nFirstIndex += baseIndex; } else { // by default we draw the whole thing
nFirstIndex = baseIndex; if( m_IndexOverride ) { nIndexCount = m_pIndexBuffer->IndexCount(); Assert( ( m_Type != MATERIAL_POINTS ) && ( m_Type != MATERIAL_INSTANCED_QUADS ) ); 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 = numVertices; } 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( pVecDiffuseModulation );
s_pPrims = NULL; } }
void CDynamicMeshDX8::DrawModulated( const Vector4D &vecDiffuseModulation, int nFirstIndex, int nIndexCount ) { DrawInternal( &vecDiffuseModulation, nFirstIndex, nIndexCount ); }
void CDynamicMeshDX8::Draw( int nFirstIndex, int nIndexCount ) { DrawInternal( NULL, nFirstIndex, nIndexCount ); }
//-----------------------------------------------------------------------------
// 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( NULL ); }
// 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, bool bHasVertexOverride, bool bHasIndexOverride ) { CBaseMeshDX8::SetVertexFormat(format, bHasVertexOverride, bHasIndexOverride); 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 = (unsigned short*)( g_pScratchIndexBuffer ); 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, MeshBuffersAllocationSettings_t *pSettings ) { 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 = (unsigned short*)( g_pScratchIndexBuffer ); 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::DrawInternal( const Vector4D *pVecDiffuseModulation, 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( pVecDiffuseModulation );
// 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::Draw( int nFirstIndex, int nIndexCount ) { DrawInternal( NULL, nFirstIndex, nIndexCount ); }
void CTempMeshDX8::DrawModulated( const Vector4D &vecDiffuseModulation, int nFirstIndex, int nIndexCount ) { DrawInternal( &vecDiffuseModulation, nFirstIndex, nIndexCount ); }
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 = &g_MeshMgr.m_ModifyBuilder;
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( const unsigned char *pInstanceCommandBuffer ) { Assert( m_InPass );
#ifdef DBGFLAG_ASSERT
m_InPass = false; #endif
// Done building the mesh
g_MeshMgr.m_ModifyBuilder.End();
// Have the dynamic mesh render a single pass...
GetDynamicMesh()->DrawSinglePassImmediately(); }
//-----------------------------------------------------------------------------
//
// Mesh manager implementation
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CMeshMgr::CMeshMgr() : m_pDynamicIndexBuffer(0), m_DynamicTempMesh(true), m_pVertexIDBuffer(0), m_pEmptyColorBuffer(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; memset( m_pPreTessPatchIndexBuffer, 0, sizeof(m_pPreTessPatchIndexBuffer) ); memset( m_pPreTessPatchVertexBuffer, 0, sizeof(m_pPreTessPatchVertexBuffer) ); m_pZeroVertexBuffer = NULL; }
CMeshMgr::~CMeshMgr() { }
//-----------------------------------------------------------------------------
// Initialize, shutdown
//-----------------------------------------------------------------------------
void CMeshMgr::Init() { #ifdef _GAMECONSOLE
s_nMemoryFrame = 0; for ( int i = 0; i < MAX_TEMP_BUFFER; ++i ) { // NOTE: Debugging modes consume a bunch of this. Need only 64 when not using them.
static int nStackCount = 0; CFmtStr stackName( "CMeshMgr::s_BufferMemory[%d]", nStackCount++ ); s_BufferMemory[i].Init( (const char *)stackName, ( IsPS3() ? 2 /* PS3 allocates more objects */ : 1 ) * 256 * 1024, 32 * 1024, 32 * 1024 ); } #endif
m_DynamicMesh.Init( 0 ); m_DynamicFlexMesh.Init( 1 );
// The dynamic index buffer
m_pDynamicIndexBuffer = new CIndexBuffer( Dx9Device(), INDEX_BUFFER_SIZE, ShaderAPI()->UsingSoftwareVertexProcessing(), true );
// If we're running in vs3.0, allocate a vertexID buffer
CreateVertexIDBuffer(); CreateZeroVertexBuffer(); CreateEmptyColorBuffer();
// If we're running in vs3.0, allocate index and vertex buffers for pre-tessellated patches
CreatePreTessPatchIndexBuffers(); CreatePreTessPatchVertexBuffers();
// Track these 2 allocations as well.
g_VBAllocTracker->TrackMeshAllocations( "CreateDynamicIndexBuffers" ); m_DynamicIndexBuffer.Allocate(); g_VBAllocTracker->TrackMeshAllocations( NULL );
g_VBAllocTracker->TrackMeshAllocations( "CreateDynamicVertexBuffers" ); m_DynamicVertexBuffer.Allocate(); g_VBAllocTracker->TrackMeshAllocations( NULL ); }
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() { if ( m_pDynamicIndexBuffer ) { delete m_pDynamicIndexBuffer; m_pDynamicIndexBuffer = 0; }
DestroyVertexBuffers();
// If we're running in vs3.0, destroy a vertexID buffer
DestroyZeroVertexBuffer(); DestroyVertexIDBuffer(); DestroyEmptyColorBuffer(); DestroyPreTessPatchIndexBuffers(); DestroyPreTessPatchVertexBuffers();
#ifdef _GAMECONSOLE
m_ExternalMesh.CleanUp(); m_ExternalFlexMesh.CleanUp(); // if we need m_ExternalIndexBuffer.CleanUp(), this would be the place to call it
#endif
m_DynamicIndexBuffer.Free(); m_DynamicVertexBuffer.Free(); }
//-----------------------------------------------------------------------------
// Fills a vertexID buffer
//-----------------------------------------------------------------------------
void CMeshMgr::FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount ) { if ( IsGameConsole() ) 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 ); }
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; } }
//-----------------------------------------------------------------------------
// Creates, destroys the vertexID buffer
//-----------------------------------------------------------------------------
void CMeshMgr::CreateVertexIDBuffer() { if ( IsGameConsole() ) return;
DestroyVertexIDBuffer();
// Track mesh allocations
g_VBAllocTracker->TrackMeshAllocations( "CreateVertexIDBuffer" ); if ( g_pHardwareConfig->ActualHasFastVertexTextures() ) { 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::DestroyVertexIDBuffer() { if ( m_pVertexIDBuffer ) { delete m_pVertexIDBuffer; m_pVertexIDBuffer = NULL; } }
CVertexBuffer *CMeshMgr::GetVertexIDBuffer( ) { return m_pVertexIDBuffer; }
//--------------------------------------------------------------------------------------
// Helper to get the number of vertices to draw with respect to the subdivison
// amount requested.
//--------------------------------------------------------------------------------------
int CMeshMgr::GetNumIndicesForSubdivisionLevel( int iSubdivLevel ) { return ( ( ( iSubdivLevel + 1 ) * 2 + 2 ) * iSubdivLevel ) - 2; }
//--------------------------------------------------------------------------------------
// Create an index buffer for a patch of a certain size
//--------------------------------------------------------------------------------------
void CMeshMgr::FillPreTessPatchIB( CIndexBuffer* pIndexBuffer, int iSubdivLevel, int nIndexCount ) { if ( IsGameConsole() ) return;
// Fill the buffer with the values 0->(nCount-1)
int nBaseIndexIndex = 0; unsigned short *pIndices = ( unsigned short* )pIndexBuffer->Lock( false, nIndexCount, nBaseIndexIndex ); if ( !pIndices ) return;
unsigned short iVertIndex = 0; UINT iIndex = 0; for( int v = 0; v < iSubdivLevel; v++ ) { iVertIndex = ( unsigned short )( ( iSubdivLevel + 1 ) * ( iSubdivLevel - v ) ); for( int u = 0; u < iSubdivLevel + 1; ++u ) { *pIndices = iVertIndex; pIndices ++; *pIndices = iVertIndex - ( unsigned short )( iSubdivLevel + 1 ); pIndices ++; iVertIndex++; } if( v != iSubdivLevel - 1 ) { // add a degenerate tri for stripping
*pIndices = pIndices[iIndex - 1]; pIndices ++; *pIndices = ( unsigned short )( ( iSubdivLevel + 1 ) * ( iSubdivLevel - ( v + 1 ) ) ); pIndices ++; } }
pIndexBuffer->Unlock( nIndexCount ); }
//--------------------------------------------------------------------------------------
// Create an vertex buffer for a patch of a certain size.
// This patch will contain precalculated UVs and bernstein polynomial coefficients.
//--------------------------------------------------------------------------------------
void CMeshMgr::FillPreTessPatchVB( CVertexBuffer* pVertexBuffer, int iSubdivLevel, int nVertexCount ) { if ( IsGameConsole() ) return;
// Fill the buffer with the values 0->(nCount-1)
int nBaseVertexIndex = 0;
PreTessPatchVertex_t *pVertices = ( PreTessPatchVertex_t* )pVertexBuffer->Lock( nVertexCount, nBaseVertexIndex ); if ( !pVertices ) return;
// Find our step values
float uDelta = 1.0f / ( float )iSubdivLevel; float vDelta = 1.0f / ( float )iSubdivLevel;
// Loop through terrain vertices and get height from the heightmap
float vStart = 0.0f; for( int v = 0; v < iSubdivLevel + 1; ++v ) { float uStart = 0.0f; for( int u = 0; u < iSubdivLevel + 1; ++u ) { // patch UV parametric coordinates
pVertices->m_vPatchUV.x = uStart; pVertices->m_vPatchUV.y = vStart;
// Regular basis functions
pVertices->m_vBasisU.x = CubicBasis0( uStart ); pVertices->m_vBasisU.y = CubicBasis1( uStart ); pVertices->m_vBasisU.z = CubicBasis2( uStart ); pVertices->m_vBasisU.w = CubicBasis3( uStart );
pVertices->m_vBasisV.x = CubicBasis0( vStart ); pVertices->m_vBasisV.y = CubicBasis1( vStart ); pVertices->m_vBasisV.z = CubicBasis2( vStart ); pVertices->m_vBasisV.w = CubicBasis3( vStart );
pVertices ++; uStart += uDelta; } vStart += vDelta; }
pVertexBuffer->Unlock( nVertexCount ); }
//-----------------------------------------------------------------------------
// Fills a vertexID buffer
//-----------------------------------------------------------------------------
void CMeshMgr::FillEmptyColorBuffer( CVertexBuffer *pEmptyColorBuffer, int nCount ) { // Fill the buffer with the values 0->(nCount-1)
int nBaseVertexIndex = 0; D3DCOLOR *pBuffer = (D3DCOLOR*)pEmptyColorBuffer->Lock( nCount, nBaseVertexIndex ); memset( pBuffer, 0, nCount * sizeof(D3DCOLOR) ); pEmptyColorBuffer->Unlock( nCount ); }
//-----------------------------------------------------------------------------
// Creates, destroys a fake color mesh
//-----------------------------------------------------------------------------
void CMeshMgr::CreateEmptyColorBuffer() { DestroyEmptyColorBuffer();
// Track mesh allocations
g_VBAllocTracker->TrackMeshAllocations( "CreateEmptyColorMesh" ); m_pEmptyColorBuffer = new CVertexBuffer( Dx9Device(), 0, 0, sizeof(D3DCOLOR), VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() ); FillEmptyColorBuffer( m_pEmptyColorBuffer, VERTEX_BUFFER_SIZE );
g_VBAllocTracker->TrackMeshAllocations( NULL ); }
void CMeshMgr::DestroyEmptyColorBuffer() { if ( m_pEmptyColorBuffer ) { delete m_pEmptyColorBuffer; m_pEmptyColorBuffer = NULL; } }
CVertexBuffer *CMeshMgr::GetEmptyColorBuffer( ) { return m_pEmptyColorBuffer; }
//-----------------------------------------------------------------------------
// Create pre-tessellated patch index buffers
//-----------------------------------------------------------------------------
void CMeshMgr::CreatePreTessPatchIndexBuffers() { // Don't do instanced tessellation on console platforms (360 or ps3)
if ( IsGameConsole() ) return;
DestroyPreTessPatchIndexBuffers();
if ( g_pHardwareConfig->ActualHasFastVertexTextures() ) { for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i ) { int iSubdivLevel = i + 1; int nIndexCount = GetNumIndicesForSubdivisionLevel( iSubdivLevel ); m_pPreTessPatchIndexBuffer[i] = new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing(), false );
FillPreTessPatchIB( m_pPreTessPatchIndexBuffer[i], iSubdivLevel, nIndexCount ); } } }
//-----------------------------------------------------------------------------
// Create pre-tessellated patch vertex buffers
//-----------------------------------------------------------------------------
void CMeshMgr::CreatePreTessPatchVertexBuffers() { // Don't do instanced tessellation on console platforms (360 or ps3)
if ( IsGameConsole() ) return;
DestroyPreTessPatchVertexBuffers();
// Track mesh allocations
g_VBAllocTracker->TrackMeshAllocations( "CreatePreTessPatchVertexBuffers" ); if ( g_pHardwareConfig->ActualHasFastVertexTextures() ) { for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i ) { int iSubdivLevel = i + 1; int nVertexCount = iSubdivLevel + 1; nVertexCount *= nVertexCount;
m_pPreTessPatchVertexBuffer[i] = new CVertexBuffer( Dx9Device(), 0, 0, sizeof( PreTessPatchVertex_t ), nVertexCount, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() );
FillPreTessPatchVB( m_pPreTessPatchVertexBuffer[i], iSubdivLevel, nVertexCount ); } } g_VBAllocTracker->TrackMeshAllocations( NULL ); }
//-----------------------------------------------------------------------------
// Destroy pre-tessellated patch index / vertex buffers
//-----------------------------------------------------------------------------
void CMeshMgr::DestroyPreTessPatchIndexBuffers() { for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i ) { if( m_pPreTessPatchIndexBuffer[i] ) { delete m_pPreTessPatchIndexBuffer[i]; m_pPreTessPatchIndexBuffer[i] = NULL; } } } void CMeshMgr::DestroyPreTessPatchVertexBuffers() { for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i ) { if( m_pPreTessPatchVertexBuffer[i] ) { delete m_pPreTessPatchVertexBuffer[i]; m_pPreTessPatchVertexBuffer[i] = NULL; } } }
CIndexBuffer *CMeshMgr::GetPreTessPatchIndexBuffer( int iSubdivLevel ) { Assert( iSubdivLevel > -1 && iSubdivLevel < MAX_TESS_DIVISIONS_PER_SIDE ); Assert( m_pPreTessPatchIndexBuffer[ iSubdivLevel ] ); return m_pPreTessPatchIndexBuffer[ iSubdivLevel ]; }
CVertexBuffer *CMeshMgr::GetPreTessPatchVertexBuffer( int iSubdivLevel ) { Assert( iSubdivLevel > -1 && iSubdivLevel < MAX_TESS_DIVISIONS_PER_SIDE ); Assert( m_pPreTessPatchVertexBuffer[ iSubdivLevel ] ); return m_pPreTessPatchVertexBuffer[ iSubdivLevel ]; }
//-----------------------------------------------------------------------------
// 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 ); } } }
//-----------------------------------------------------------------------------
// Allocate temporary arrays either on the stack, or from the heap.
// Prevents using all the stack when *lots* of objects are rendered to CSM's.
//-----------------------------------------------------------------------------
#if defined( CSTRIKE15 ) // 7ls && !defined( _GAMECONSOLE )
#define STUDIORENDER_TEMP_DATA_MALLOC( typeName, p, n ) const int nTempDataSize##p = (n); void *pvFree##p = NULL; typeName *p = (typeName *) ( ( nTempDataSize##p < 64*1024 ) ? stackalloc( nTempDataSize##p ) : ( pvFree##p = malloc( nTempDataSize##p ) ) );
#define STUDIORENDER_TEMP_DATA_FREE( p ) free( pvFree##p )
#else
#define STUDIORENDER_TEMP_DATA_MALLOC( typeName, p, n ) typeName *p = (typeName *) stackalloc(n);
#define STUDIORENDER_TEMP_DATA_FREE( p )
#endif
//-----------------------------------------------------------------------------
// Draws instanced meshes
//-----------------------------------------------------------------------------
void CMeshMgr::DrawInstances( int nInstanceCount, const MeshInstanceData_t *pInstanceData ) { if ( nInstanceCount == 0 ) return;
// FIXME: Queue?!?
// if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) )
// {
// MarkAsDrawn();
// return;
// }
// can't do these in selection mode!
Assert( !ShaderAPI()->IsInSelectionMode() );
// NOTE: on the 360/PS3, we can't do too many instances at the same time because
// we overflow the stack in the compiled state allocation.
int nBatchSize = IsGameConsole() ? MIN( nInstanceCount, CONSOLE_MAX_MODEL_FAST_PATH_BATCH_SIZE ) : nInstanceCount;
// Compute info necessary for the entire render
const int nCompiledStateSize = nBatchSize * sizeof(CompiledLightingState_t); STUDIORENDER_TEMP_DATA_MALLOC( CompiledLightingState_t, pCompiledState, nCompiledStateSize );
const int nInfoSize = nBatchSize * sizeof(InstanceInfo_t); STUDIORENDER_TEMP_DATA_MALLOC( InstanceInfo_t, pCompiledInfo, nInfoSize );
// No flexing allowed if rendering instances
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); VertexCompressionType_t nCompression = CompressionType( pInstanceData[0].m_pVertexBuffer->GetVertexFormat() );
#ifdef _DEBUG
for ( int i = 0; i < nInstanceCount; ++i ) { Assert( nCompression == CompressionType( pInstanceData[i].m_pVertexBuffer->GetVertexFormat() ) ); } #endif
g_pInstanceCompiledState = pCompiledState; g_pInstanceInfo = pCompiledInfo; while ( nInstanceCount > 0 ) { memset( pCompiledInfo, 0, nBatchSize * sizeof(InstanceInfo_t) );
g_nInstanceCount = nBatchSize; g_pInstanceData = pInstanceData;
// This is going to cause RenderPass to get called a bunch
ShaderAPI()->DrawMesh( NULL, nBatchSize, pInstanceData, nCompression, pCompiledState, pCompiledInfo );
nInstanceCount -= nBatchSize; pInstanceData += nBatchSize; nBatchSize = MIN( nInstanceCount, CONSOLE_MAX_MODEL_FAST_PATH_BATCH_SIZE ); }
g_pInstanceCompiledState = NULL; g_pInstanceInfo = NULL; g_nInstanceCount = 0; g_pInstanceData = NULL;
STUDIORENDER_TEMP_DATA_FREE( pCompiledInfo ); STUDIORENDER_TEMP_DATA_FREE( pCompiledState ); }
//-----------------------------------------------------------------------------
// Is the mesh dynamic?
//-----------------------------------------------------------------------------
bool CMeshMgr::IsDynamicMesh( IMesh* pMesh ) const { return ( pMesh == &m_DynamicMesh ) || ( pMesh == &m_DynamicFlexMesh ); }
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(); }
#ifdef _GAMECONSOLE
// Unbind everything. We're going to be decommitting memory
// and we don't want the slightest chance that there could be
// D3D internal state pointing at this memory
// (defensive fix for tracker bug 49836)
for ( int i = 0; i < 4; ++i ) { D3DSetStreamSource( i, 0, 0, 0 ); }
g_bUsingVertexID = false; g_nLastVertexIDOffset = -1; g_bUsingPreTessPatches = false; g_pLastStreamSpec = NULL; g_pLastVertex = NULL; g_pLastVertexBuffer = NULL; g_nLastVertOffsetInBytes = -1; g_pLastColorBuffer = NULL; g_nLastColorMeshVertOffsetInBytes = 0;
if ( ++s_nMemoryFrame >= MAX_TEMP_BUFFER ) { s_nMemoryFrame = 0; }
/*
static int s_nHisto[ 33 ]; static int s_nHistoCount; int nMem = s_BufferMemory[s_nMemoryFrame].GetUsed() / ( 32 * 1024 ); s_nHisto[ nMem ]++; if ( ( ++s_nHistoCount % 1024 ) == 0 ) { Msg( "DynamicBuffers: " ); bool bFound = false; for( int i = 32; i >= 0; --i ) { if ( s_nHisto[i] ) { bFound = true; } if ( !bFound ) continue; Msg( "[%dk %d] ", i * 32, s_nHisto[i] ); } Msg( "\n" ); } */ s_BufferMemory[s_nMemoryFrame].FreeAll(); #endif
}
//-----------------------------------------------------------------------------
// Releases all dynamic vertex buffers
//-----------------------------------------------------------------------------
void CMeshMgr::DestroyVertexBuffers() { g_bUsingVertexID = false; g_nLastVertexIDOffset = -1; g_bUsingPreTessPatches = false; g_pLastStreamSpec = NULL; g_pLastVertex = NULL; g_pLastVertexBuffer = NULL; g_nLastVertOffsetInBytes = -1; g_pLastColorBuffer = NULL; g_nLastColorMeshVertOffsetInBytes = 0;
if( !Dx9Device() ) { return; // Dx device wasn't initialized yet, we have nothing to clean up
}
// 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(); }
//-----------------------------------------------------------------------------
// Creates, destroys static meshes
//-----------------------------------------------------------------------------
IMesh* CMeshMgr::CreateStaticMesh( VertexFormat_t format, const char *pTextureBudgetGroup, IMaterial * pMaterial, VertexStreamSpec_t *pStreamSpec ) { // FIXME: Use a fixed-size allocator
CMeshDX8* pNewMesh = new CMeshDX8( pTextureBudgetGroup ); pNewMesh->SetVertexStreamSpec( pStreamSpec ); pNewMesh->SetVertexFormat( format, false, false ); if ( pMaterial != NULL ) { 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, false, false ); return &m_DynamicMesh; }
//-----------------------------------------------------------------------------
// Copy a static mesh index buffer to a dynamic mesh index buffer
//-----------------------------------------------------------------------------
void CMeshMgr::CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, CMeshDX8 *pSrcIndexMesh ) { 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->GetDXSupportLevel() >= 92 ) { // FIXME: Kinda ugly size.. 28 bytes
m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_WRINKLE | VERTEX_FORMAT_USE_EXACT_FORMAT, false, false ); } else { // Same size as a pair of float3s (24 bytes)
m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_USE_EXACT_FORMAT, false, false ); } return &m_DynamicFlexMesh; }
//-----------------------------------------------------------------------------
// Gets at the dynamic mesh
//-----------------------------------------------------------------------------
IMesh* CMeshMgr::GetDynamicMesh( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) { Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );
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; }
// HACK: SetVertexFormat here will slam both the vertex + index buffer
// to use the default. Some patterns actually do the insane thing of
// passing the dynamic mesh as its own override (BindBatch, for example).
// Cache off the override buffers before this happens (in SetVertexFormat of all things)
CBaseMeshDX8* pBaseVertexOverride = static_cast<CBaseMeshDX8*>( pVertexOverride ); CBaseMeshDX8* pBaseIndexOverride = static_cast<CBaseMeshDX8*>( pIndexOverride ); CVertexBuffer *pVertexOverrideBuffer = ( pBaseVertexOverride ) ? pBaseVertexOverride->GetVertexBuffer() : NULL; CIndexBuffer *pIndexOverrideBuffer = ( pBaseIndexOverride ) ? pBaseIndexOverride->GetIndexBuffer() : NULL;
if( !pBaseVertexOverride ) { // 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, false, ( pIndexOverrideBuffer != NULL ) ); } else { pMesh->SetVertexFormat( pBaseVertexOverride->GetVertexFormat(), true, ( pIndexOverrideBuffer != NULL ) ); } 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 ) { if ( pVertexOverrideBuffer ) { m_DynamicMesh.OverrideVertexBuffer( pVertexOverrideBuffer ); } if ( pIndexOverrideBuffer ) { m_DynamicMesh.OverrideIndexBuffer( pIndexOverrideBuffer ); } }
return pMesh; }
//-----------------------------------------------------------------------------
// Used to construct vertex data
//-----------------------------------------------------------------------------
void CMeshMgr::ComputeVertexDescription( unsigned char* pBuffer, VertexFormat_t vertexFormat, MeshDesc_t& desc ) const { ComputeVertexDesc< false >( 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, VERTEX_MAX_TEXTURE_COORDINATES ); for ( int i = 0; i < nTexCoordArraySize; ++i ) { if ( pTexCoordDimensions ) { Assert( pTexCoordDimensions[i] >= 0 && pTexCoordDimensions[i] <= 4 ); fmt |= VERTEX_TEXCOORD_SIZE( i, pTexCoordDimensions[i] ); } else { fmt |= VERTEX_TEXCOORD_SIZE( 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; }
// 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; if ( fmt == 0 ) { Warning( "bad vertex size for material %s\n", pMaterial->GetName() ); return 65535; }
int nMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / VertexFormatSize( fmt ); if ( nMaxVerts > 65535 ) { nMaxVerts = 65535; } return nMaxVerts; }
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 ); if ( !m_DynamicVertexBuffers[nIndex].m_pBuffer ) { MemOutOfMemory( sizeof(CVertexBuffer) ); } 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::GetDynamicIndexBufferInternal() { return m_pDynamicIndexBuffer; }
#ifdef _GAMECONSOLE
int CMeshMgr::GetDynamicIndexBufferAllocationCount() { if ( !GetDynamicIndexBufferInternal() ) { return 0; }
return GetDynamicIndexBufferInternal()->AllocationCount(); }
int CMeshMgr::GetDynamicIndexBufferIndicesLeft() { if ( !GetDynamicIndexBufferInternal() ) { return 0; }
return GetDynamicIndexBufferInternal()->GetIndicesLeft(); }
//-----------------------------------------------------------------------------
// Backdoor used by the queued context to directly use write-combined memory
//-----------------------------------------------------------------------------
IMesh *CMeshMgr::GetExternalMesh( const ExternalMeshInfo_t& info ) { if ( info.m_bFlexMesh ) { m_ExternalFlexMesh.Init( info ); return &m_ExternalFlexMesh; } m_ExternalMesh.Init( info ); return &m_ExternalMesh; }
void CMeshMgr::SetExternalMeshData( IMesh *pMesh, const ExternalMeshData_t &data ) { CExternalMeshDX8 *pExternalMesh = assert_cast< CExternalMeshDX8* >( pMesh ); pExternalMesh->SetExternalData( data ); }
IIndexBuffer *CMeshMgr::GetExternalIndexBuffer( int nIndexCount, uint16 *pIndexData ) { m_ExternalIndexBuffer.Init( nIndexCount, pIndexData ); return &m_ExternalIndexBuffer; }
#endif // _GAMECONSOLE
IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( 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 ); if ( !pNewVertexBuffer ) { MemOutOfMemory( sizeof(CVertexBuffer) ); } 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 ( CompressionType( vertexFormat ) != VERTEX_COMPRESSION_NONE ) { // UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing)
DebuggerBreak(); return NULL; }
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; }
return pVertexBuffer; }
IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer() { #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
return &m_DynamicIndexBuffer; }
void CMeshMgr::SetVertexIDStreamState( int nIDOffsetBytes ) { if ( IsGameConsole() ) 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; g_nLastVertexIDOffset = nIDOffsetBytes; } }
void CMeshMgr::SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel ) { // empty for now
}
void CMeshMgr::SetCustomStreamsState() { if ( g_pLastStreamSpec ) { for ( int k = 0; k < ARRAYSIZE( g_bCustomStreamsSet ); ++ k ) { if ( g_bCustomStreamsSet[k] ) { RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); RECORD_INT( -1 ); // vertex buffer id
RECORD_INT( k ); // stream
RECORD_INT( 0 ); // vertex offset
RECORD_INT( 0 ); // vertex size
D3DSetStreamSource( k, 0, 0, 0 );
g_bCustomStreamsSet[k] = false; } } } g_pLastStreamSpec = NULL; }
void CMeshMgr::SetColorStreamState() { if ( g_pLastColorBuffer ) { 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_pLastColorBuffer = NULL; g_nLastColorMeshVertOffsetInBytes = 0; }
void CMeshMgr::SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride ) { // Calls in here assume shader support...
// HACK...point stream 2 at the same VB which is bound to stream 0...
//Assert( m_pCurrentVertexBuffer && m_pCurrentVertexBuffer->GetDx9Buffer() );
//D3DSetStreamSource( 2, m_pCurrentVertexBuffer->GetDx9Buffer(), nVertOffsetInBytes, nVertexStride );
// 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
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
// 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( 0 ); SetColorStreamState(); SetCustomStreamsState(); SetVertexStreamState( nVertexOffsetInBytes, nVertexStride ); SetIndexStreamState( nFirstVertexIdx ); SetTessellationStreamState( nVertexOffsetInBytes, 1 );
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( !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(); }
//-----------------------------------------------------------------------------
// Actually does the dirty deed of rendering
//-----------------------------------------------------------------------------
void CMeshMgr::DrawInstancedPrims( const unsigned char *pInstanceCommandBuffer ) { for ( int i = 0; i < g_nInstanceCount; ++i ) { const MeshInstanceData_t &instance= g_pInstanceData[i]; int nPrimitiveCount = NumPrimitives( instance.m_nPrimType, instance.m_nIndexCount );
CMeshDX8 *pVertexMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pVertexBuffer ) );
if ( !pVertexMesh || !pVertexMesh->m_pVertexBuffer ) { Warning( "CMeshMgr::DrawInstancedPrims: Vertex buffer in not setup properly, mesh will not be rendered." ); continue; } #ifdef DX_TO_GL_ABSTRACTION
pVertexMesh->HandleLateCreation(); #endif
D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, pVertexMesh->m_pVertexBuffer->GetInterface(), instance.m_nVertexOffsetInBytes, pVertexMesh->m_pVertexBuffer->VertexSize() );
D3DSetStreamSource( 0, pVertexMesh->m_pVertexBuffer->GetInterface(), instance.m_nVertexOffsetInBytes, pVertexMesh->m_pVertexBuffer->VertexSize() );
g_pLastVertex = pVertexMesh->m_pVertexBuffer; g_nLastVertOffsetInBytes = instance.m_nVertexOffsetInBytes;
IDirect3DIndexBuffer9 *pD3DIndexBuffer; IIndexBuffer *pIndexBuffer = const_cast<IIndexBuffer*>( instance.m_pIndexBuffer ); if ( pIndexBuffer->GetMesh() ) { CMeshDX8 *pMesh = static_cast<CMeshDX8*>( pIndexBuffer ); #ifdef DX_TO_GL_ABSTRACTION
pMesh->HandleLateCreation(); #endif
pD3DIndexBuffer = pMesh->m_pIndexBuffer->GetInterface(); } else { CIndexBufferDx8 *pCIndexBuffer = static_cast<CIndexBufferDx8*>( pIndexBuffer ); pD3DIndexBuffer = pCIndexBuffer->m_pIndexBuffer; }
D3DSetIndices( pD3DIndexBuffer );
CMeshDX8 *pColorMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pColorBuffer ) ); #ifdef DX_TO_GL_ABSTRACTION
if (pColorMesh) { pColorMesh->HandleLateCreation(); } #endif
CVertexBuffer *pVertexBuffer = pColorMesh ? pColorMesh->GetVertexBuffer() : m_pEmptyColorBuffer; int nVertexOffset = pColorMesh ? instance.m_nColorVertexOffsetInBytes : 0;
// Set vertex decl
VertexFormat_t nMeshFormat = pVertexMesh->GetVertexFormat();
IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial(); bool bUseColorMesh = ( pMaterial->GetVertexFormat() & VERTEX_COLOR_STREAM_1 ) != 0; ShaderAPI()->SetVertexDecl( nMeshFormat, bUseColorMesh, false, false, false, NULL );
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SPECULAR1, pVertexBuffer->GetInterface(), nVertexOffset, pVertexBuffer->VertexSize() );
D3DPRIMITIVETYPE nMode = ComputeMode( instance.m_nPrimType );
if ( pInstanceCommandBuffer ) { ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, i, true ); } else { ShaderAPI()->SetSkinningMatrices( instance ); }
{ VPROF( "Dx9Device()->DrawIndexedPrimitive" ); VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 ); VPROF_INCREMENT_COUNTER( "numPrimitives", nPrimitiveCount );
Dx9Device()->DrawIndexedPrimitive( nMode, 0, 0, pVertexMesh->VertexCount(), instance.m_nIndexOffset, nPrimitiveCount ); } } }
void CMeshMgr::RenderPassForInstances( const unsigned char *pInstanceCommandBuffer ) { VPROF( "CMeshMgr::RenderPassForInstances" );
IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial(); bool bUsingVertexID = pMaterial->IsUsingVertexID(); bool bUseColorMesh = ( pMaterial->GetVertexFormat() & VERTEX_COLOR_STREAM_1 ) != 0;
for ( int i = 0; i < g_nInstanceCount; ++i ) { const MeshInstanceData_t &instance = g_pInstanceData[i]; Assert( ( instance.m_nPrimType != MATERIAL_POINTS ) && ( instance.m_nPrimType != MATERIAL_INSTANCED_QUADS ) ); int nPrimitiveCount = NumPrimitives( instance.m_nPrimType, instance.m_nIndexCount );
// make sure the vertex format is a superset of the current material's
// vertex format...
CMeshDX8 *pVertexMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pVertexBuffer ) ); #ifdef DX_TO_GL_ABSTRACTION
pVertexMesh->HandleLateCreation(); #endif
Assert( pVertexMesh ); VertexFormat_t nMeshFormat = pVertexMesh->GetVertexFormat(); if ( !IsValidVertexFormat( nMeshFormat, pMaterial ) ) { Warning( "Material %s does not support vertex format used by the mesh\n" "(maybe missing fields or mismatched vertex compression?),\n" "mesh will not be rendered. Grab a programmer!\n", pMaterial->GetName() ); continue; }
// FIXME: solve problems when using CVertexBufferDx8 instead of meshes
pVertexMesh->SetVertexStreamState( instance.m_nVertexOffsetInBytes, true );
IIndexBuffer *pIndexBuffer = const_cast<IIndexBuffer*>( instance.m_pIndexBuffer ); if ( pIndexBuffer->GetMesh() ) { CMeshDX8 *pMesh = static_cast<CMeshDX8*>( pIndexBuffer ); Assert( pMesh ); #ifdef DX_TO_GL_ABSTRACTION
pMesh->HandleLateCreation(); #endif
pMesh->SetIndexStreamState( 0 ); } else { CIndexBufferDx8 *pCIndexBuffer = static_cast<CIndexBufferDx8*>( pIndexBuffer ); Assert( pCIndexBuffer ); pCIndexBuffer->SetIndexStreamState( 0 ); }
CMeshDX8 *pColorMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pColorBuffer ) ); #ifdef DX_TO_GL_ABSTRACTION
if ( pColorMesh ) { pColorMesh->HandleLateCreation(); } #endif
CVertexBuffer *pVertexBuffer = pColorMesh ? pColorMesh->GetVertexBuffer() : m_pEmptyColorBuffer; int nVertexOffset = pColorMesh ? instance.m_nColorVertexOffsetInBytes : 0; D3DSetStreamSource( VertexStreamSpec_t::STREAM_SPECULAR1, pVertexBuffer->GetInterface(), nVertexOffset, pVertexBuffer->VertexSize() ); pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); g_pLastColorBuffer = pVertexBuffer; g_nLastColorMeshVertOffsetInBytes = nVertexOffset;
bool bUsingPreTessPatches = ( pVertexMesh->GetTessellationType() > 0 ) && ( ShaderAPI()->GetTessellationMode() == TESSELLATION_MODE_ACC_PATCHES_EXTRA || ShaderAPI()->GetTessellationMode() == TESSELLATION_MODE_ACC_PATCHES_REG ); ShaderAPI()->SetVertexDecl( nMeshFormat, bUseColorMesh, NULL, bUsingVertexID, bUsingPreTessPatches, NULL );
D3DPRIMITIVETYPE nMode = ComputeMode( instance.m_nPrimType );
#ifdef CHECK_INDICES
CheckIndices( nMode, 0, pVertexMesh->VertexCount(), 0, instance.m_nIndexOffset, nPrimitiveCount ); #endif // CHECK_INDICES
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, i, true );
VPROF( "Dx9Device()->DrawIndexedPrimitive" ); VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 ); VPROF_INCREMENT_COUNTER( "numPrimitives", nPrimitiveCount );
Dx9Device()->DrawIndexedPrimitive( nMode, // Member of the D3DPRIMITIVETYPE enumerated type, describing the type
// of primitive to render. D3DPT_POINTLIST is not supported with this method.
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.
0, // 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.
pVertexMesh->VertexCount(), // Number of vertices used during this call. The first vertex
// is located at index: BaseVertexIndex + MinIndex.
instance.m_nIndexOffset, // Index of the first index to use when accesssing the vertex buffer.
// Beginning at StartIndex to index vertices from the vertex buffer.
nPrimitiveCount ); // Number of primitives to render. The number of vertices used
// is a function of the primitive count and the primitive type.
} }
void CMeshMgr::RenderPassWithVertexAndIndexBuffers( const unsigned char *pInstanceCommandBuffer ) { // LOCK_SHADERAPI(); MESHFIXME
VPROF( "CShaderAPIDX8::RenderPassWithVertexAndIndexBuffers" );
if ( g_nInstanceCount ) { RenderPassForInstances( pInstanceCommandBuffer ); return; }
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_pLastColorBuffer - 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 ) { D3DSetIndices( pDx9Buffer ); pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
g_pLastIndex = NULL; g_LastVertexIdx = -1; } }
|