You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5980 lines
172 KiB
5980 lines
172 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//===========================================================================// |
|
|
|
#define DISABLE_PROTECTED_THINGS |
|
#include "locald3dtypes.h" |
|
#include "imeshdx8.h" |
|
#include "shaderapidx8_global.h" |
|
#include "materialsystem/IShader.h" |
|
#include "tier0/vprof.h" |
|
#include "studio.h" |
|
#include "tier1/fmtstr.h" |
|
|
|
#include "tier0/platform.h" |
|
#include "tier0/systeminformation.h" |
|
|
|
// fixme - stick this in a header file. |
|
#if defined( _DEBUG ) && !defined( _X360 ) |
|
// define this if you want to range check all indices when drawing |
|
#define CHECK_INDICES |
|
#endif |
|
#ifdef CHECK_INDICES |
|
#define CHECK_INDICES_MAX_NUM_STREAMS 2 |
|
#endif |
|
|
|
#include "dynamicib.h" |
|
#include "dynamicvb.h" |
|
#include "utlvector.h" |
|
#include "shaderapi/ishaderapi.h" |
|
#include "imaterialinternal.h" |
|
#include "imaterialsysteminternal.h" |
|
#include "shaderapidx8.h" |
|
#include "shaderapi/ishaderutil.h" |
|
#include "materialsystem/imaterialsystemhardwareconfig.h" |
|
#include "materialsystem/materialsystem_config.h" |
|
#include "materialsystem/ivballoctracker.h" |
|
#include "tier1/strtools.h" |
|
#include "convar.h" |
|
#include "shaderdevicedx8.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Uncomment this to test buffered state |
|
//----------------------------------------------------------------------------- |
|
//#define DEBUG_BUFFERED_MESHES 1 |
|
|
|
#define MAX_DX8_STREAMS 16 |
|
|
|
#define VERTEX_FORMAT_INVALID 0xFFFFFFFFFFFFFFFFull |
|
|
|
// this is hooked into the engines convar |
|
extern ConVar mat_debugalttab; |
|
|
|
//#define DRAW_SELECTION 1 |
|
static bool g_bDrawSelection = true; // only used in DRAW_SELECTION |
|
static unsigned short g_nScratchIndexBuffer[6]; // large enough for a fast quad; used when device is not active |
|
#ifdef _DEBUG |
|
int CVertexBuffer::s_BufferCount = 0; |
|
int CIndexBuffer::s_BufferCount = 0; |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Important enumerations |
|
//----------------------------------------------------------------------------- |
|
enum |
|
{ |
|
VERTEX_BUFFER_SIZE = 32768, |
|
MAX_QUAD_INDICES = 16384, |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Code related to vertex buffers start here |
|
// |
|
//----------------------------------------------------------------------------- |
|
class CVertexBufferDx8 : public CVertexBufferBase |
|
{ |
|
typedef CVertexBufferBase BaseClass; |
|
|
|
// Methods of IVertexBuffer |
|
public: |
|
virtual int VertexCount() const; |
|
virtual VertexFormat_t GetVertexFormat() const; |
|
virtual bool IsDynamic() const; |
|
virtual void BeginCastBuffer( VertexFormat_t format ); |
|
virtual void EndCastBuffer( ); |
|
virtual int GetRoomRemaining() const; |
|
virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ); |
|
virtual void Unlock( int nVertexCount, VertexDesc_t &desc ); |
|
|
|
public: |
|
// constructor |
|
CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ); |
|
virtual ~CVertexBufferDx8(); |
|
|
|
// Allocates, deallocates the index buffer |
|
bool Allocate( ); |
|
void Free(); |
|
|
|
// Returns the vertex size |
|
int VertexSize() const; |
|
|
|
// Only used by dynamic buffers, indicates the next lock should perform a discard. |
|
void Flush(); |
|
|
|
// Returns the D3D buffer |
|
IDirect3DVertexBuffer9* GetDx9Buffer(); |
|
|
|
// Used to measure how much static buffer memory is touched each frame |
|
void HandlePerFrameTextureStats( int nFrame ); |
|
|
|
protected: |
|
IDirect3DVertexBuffer9 *m_pVertexBuffer; |
|
VertexFormat_t m_VertexFormat; |
|
int m_nVertexCount; |
|
int m_nBufferSize; |
|
int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite) |
|
|
|
// Is it locked? |
|
bool m_bIsLocked : 1; |
|
bool m_bIsDynamic : 1; |
|
bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time |
|
|
|
#ifdef VPROF_ENABLED |
|
int m_nVProfFrame; |
|
int *m_pFrameCounter; |
|
int *m_pGlobalCounter; |
|
#endif |
|
|
|
#ifdef _DEBUG |
|
static int s_nBufferCount; |
|
#endif |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Code related to index buffers start here |
|
// |
|
//----------------------------------------------------------------------------- |
|
class CIndexBufferDx8 : public CIndexBufferBase |
|
{ |
|
typedef CIndexBufferBase BaseClass; |
|
|
|
// Methods of IIndexBuffer |
|
public: |
|
virtual int IndexCount( ) const; |
|
virtual MaterialIndexFormat_t IndexFormat() const; |
|
virtual int GetRoomRemaining() const; |
|
virtual bool Lock( int nIndexCount, bool bAppend, IndexDesc_t &desc ); |
|
virtual void Unlock( int nIndexCount, IndexDesc_t &desc ); |
|
virtual void BeginCastBuffer( MaterialIndexFormat_t format ); |
|
virtual void EndCastBuffer( ); |
|
virtual bool IsDynamic() const; |
|
virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); } |
|
virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); } |
|
|
|
public: |
|
// constructor |
|
CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ); |
|
virtual ~CIndexBufferDx8(); |
|
|
|
// Allocates, deallocates the index buffer |
|
bool Allocate( ); |
|
void Free(); |
|
|
|
// Returns the index size |
|
int IndexSize() const; |
|
|
|
// Only used by dynamic buffers, indicates the next lock should perform a discard. |
|
void Flush(); |
|
|
|
// Returns the D3D buffer |
|
IDirect3DIndexBuffer9* GetDx9Buffer(); |
|
|
|
// Used to measure how much static buffer memory is touched each frame |
|
void HandlePerFrameTextureStats( int nFrame ); |
|
|
|
#ifdef CHECK_INDICES |
|
unsigned short GetShadowIndex( int i ) const; |
|
#endif |
|
|
|
private: |
|
IDirect3DIndexBuffer9 *m_pIndexBuffer; |
|
MaterialIndexFormat_t m_IndexFormat; |
|
int m_nIndexCount; |
|
int m_nBufferSize; |
|
int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite) |
|
|
|
// Is it locked? |
|
bool m_bIsLocked : 1; |
|
bool m_bIsDynamic : 1; |
|
bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time |
|
|
|
#ifdef CHECK_INDICES |
|
unsigned char *m_pShadowIndices; |
|
void *m_pLockIndexBuffer; |
|
int m_nLockIndexBufferSize; |
|
int m_nLockIndexOffset; |
|
#endif |
|
|
|
#ifdef VPROF_ENABLED |
|
int m_nVProfFrame; |
|
#endif |
|
|
|
#ifdef _DEBUG |
|
static int s_nBufferCount; |
|
#endif |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Backward compat mesh code; will go away soon |
|
// |
|
//----------------------------------------------------------------------------- |
|
abstract_class CBaseMeshDX8 : public CMeshBase |
|
{ |
|
public: |
|
// constructor, destructor |
|
CBaseMeshDX8(); |
|
virtual ~CBaseMeshDX8(); |
|
|
|
// FIXME: Make this work! Unsupported methods of IIndexBuffer + IVertexBuffer |
|
virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t& desc ) { Assert(0); return false; } |
|
virtual void Unlock( int nWrittenIndexCount, IndexDesc_t& desc ) { Assert(0); } |
|
virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); } |
|
virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); } |
|
virtual void Spew( int nIndexCount, const IndexDesc_t & desc ) { Assert(0); } |
|
virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) { Assert(0); } |
|
virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) { Assert(0); return false; } |
|
virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) { Assert(0); } |
|
virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) { Assert(0); } |
|
virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) { Assert(0); } |
|
|
|
// Locks mesh for modifying |
|
void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); |
|
void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); |
|
void ModifyEnd( MeshDesc_t& desc ); |
|
|
|
// Sets/gets the vertex format |
|
virtual void SetVertexFormat( VertexFormat_t format ); |
|
virtual VertexFormat_t GetVertexFormat() const; |
|
|
|
// Sets/gets the morph format |
|
virtual void SetMorphFormat( MorphFormat_t format ); |
|
virtual MorphFormat_t GetMorphFormat() const; |
|
// Am I using morph data? |
|
virtual bool IsUsingMorphData() const; |
|
bool IsUsingVertexID() const |
|
{ |
|
return ShaderAPI()->GetBoundMaterial()->IsUsingVertexID(); |
|
} |
|
|
|
// Sets the material |
|
virtual void SetMaterial( IMaterial* pMaterial ); |
|
|
|
// returns the # of vertices (static meshes only) |
|
int VertexCount() const { return 0; } |
|
|
|
void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes ) |
|
{ |
|
Assert( 0 ); |
|
} |
|
|
|
void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ) |
|
{ |
|
Assert( pMesh == NULL && nVertexOffsetInBytes == 0 ); |
|
} |
|
|
|
void DisableFlexMesh( ) |
|
{ |
|
Assert( 0 ); |
|
} |
|
|
|
void MarkAsDrawn() {} |
|
|
|
bool HasColorMesh( ) const { return false; } |
|
bool HasFlexMesh( ) const { return false; } |
|
|
|
// Draws the mesh |
|
void DrawMesh( ); |
|
|
|
// Begins a pass |
|
void BeginPass( ); |
|
|
|
// Spews the mesh data |
|
virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t & desc ); |
|
|
|
// Call this in debug mode to make sure our data is good. |
|
virtual void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & desc ); |
|
|
|
virtual void HandleLateCreation( ) = 0; |
|
|
|
void Draw( CPrimList *pLists, int nLists ); |
|
|
|
// Copy verts and/or indices to a mesh builder. This only works for temp meshes! |
|
virtual void CopyToMeshBuilder( |
|
int iStartVert, // Which vertices to copy. |
|
int nVerts, |
|
int iStartIndex, // Which indices to copy. |
|
int nIndices, |
|
int indexOffset, // This is added to each index. |
|
CMeshBuilder &builder ); |
|
|
|
// returns the primitive type |
|
virtual MaterialPrimitiveType_t GetPrimitiveType() const = 0; |
|
|
|
// Returns the number of indices in a mesh.. |
|
virtual int IndexCount( ) const = 0; |
|
|
|
// FIXME: Make this work! |
|
virtual MaterialIndexFormat_t IndexFormat() const { return MATERIAL_INDEX_FORMAT_16BIT; } |
|
|
|
// NOTE: For dynamic index buffers only! |
|
// Casts the memory of the dynamic index buffer to the appropriate type |
|
virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); } |
|
virtual void BeginCastBuffer( VertexFormat_t format ) { Assert(0); } |
|
virtual void EndCastBuffer( ) { Assert(0); } |
|
virtual int GetRoomRemaining() const { Assert(0); return 0; } |
|
|
|
// returns a static vertex buffer... |
|
virtual CVertexBuffer *GetVertexBuffer() { return 0; } |
|
virtual CIndexBuffer *GetIndexBuffer() { return 0; } |
|
|
|
// Do I need to reset the vertex format? |
|
virtual bool NeedsVertexFormatReset( VertexFormat_t fmt ) const; |
|
|
|
// Do I have enough room? |
|
virtual bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const; |
|
|
|
// Operation to do pre-lock |
|
virtual void PreLock() {} |
|
|
|
virtual unsigned ComputeMemoryUsed(); |
|
|
|
bool m_bMeshLocked; |
|
|
|
protected: |
|
bool DebugTrace() const; |
|
|
|
// The vertex format we're using... |
|
VertexFormat_t m_VertexFormat; |
|
|
|
// The morph format we're using |
|
MorphFormat_t m_MorphFormat; |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
IMaterialInternal* m_pMaterial; |
|
bool m_IsDrawing; |
|
#endif |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Implementation of the mesh |
|
//----------------------------------------------------------------------------- |
|
class CMeshDX8 : public CBaseMeshDX8 |
|
{ |
|
public: |
|
// constructor |
|
CMeshDX8( const char *pTextureGroupName ); |
|
virtual ~CMeshDX8(); |
|
|
|
// Locks/unlocks the mesh |
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); |
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); |
|
|
|
// Locks mesh for modifying |
|
void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); |
|
void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); |
|
void ModifyEnd( MeshDesc_t& desc ); |
|
|
|
// returns the # of vertices (static meshes only) |
|
int VertexCount() const; |
|
|
|
// returns the # of indices |
|
virtual int IndexCount( ) const; |
|
|
|
// Sets up the vertex and index buffers |
|
void UseIndexBuffer( CIndexBuffer* pBuffer ); |
|
void UseVertexBuffer( CVertexBuffer* pBuffer ); |
|
|
|
// returns a static vertex buffer... |
|
CVertexBuffer *GetVertexBuffer() { return m_pVertexBuffer; } |
|
CIndexBuffer *GetIndexBuffer() { return m_pIndexBuffer; } |
|
|
|
void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes ); |
|
void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ); |
|
void DisableFlexMesh(); |
|
|
|
virtual void HandleLateCreation( ); |
|
|
|
bool HasColorMesh( ) const; |
|
bool HasFlexMesh( ) const; |
|
|
|
// Draws the mesh |
|
void Draw( int nFirstIndex, int nIndexCount ); |
|
void Draw( CPrimList *pLists, int nLists ); |
|
void DrawInternal( CPrimList *pLists, int nLists ); |
|
|
|
// Draws a single pass |
|
void RenderPass(); |
|
|
|
// Sets the primitive type |
|
void SetPrimitiveType( MaterialPrimitiveType_t type ); |
|
MaterialPrimitiveType_t GetPrimitiveType() const; |
|
|
|
bool IsDynamic() const { return false; } |
|
|
|
protected: |
|
// Sets the render state. |
|
bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID ); |
|
|
|
// Is the vertex format valid? |
|
bool IsValidVertexFormat( VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID ); |
|
|
|
// Locks/ unlocks the vertex buffer |
|
bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ); |
|
void Unlock( int nVertexCount, VertexDesc_t &desc ); |
|
|
|
// Locks/unlocks the index buffer |
|
// Pass in nFirstIndex=-1 to lock wherever the index buffer is. Pass in a value |
|
// >= 0 to specify where to lock. |
|
int Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &pIndices ); |
|
void Unlock( int nIndexCount, IndexDesc_t &desc ); |
|
|
|
// computes how many primitives we've got |
|
int NumPrimitives( int nVertexCount, int nIndexCount ) const; |
|
|
|
// Debugging output... |
|
void SpewMaterialVerts( ); |
|
|
|
// Stream source setting methods |
|
void SetVertexIDStreamState( ); |
|
void SetColorStreamState( ); |
|
void SetVertexStreamState( int nVertOffsetInBytes ); |
|
void SetIndexStreamState( int firstVertexIdx ); |
|
|
|
void CheckIndices( CPrimList *pPrim, int numPrimitives ); |
|
|
|
// The vertex and index buffers |
|
CVertexBuffer* m_pVertexBuffer; |
|
CIndexBuffer* m_pIndexBuffer; |
|
|
|
// The current color mesh (to be bound to stream 1) |
|
// The vertex offset allows use of a global, shared color mesh VB |
|
CMeshDX8 * m_pColorMesh; |
|
int m_nColorMeshVertOffsetInBytes; |
|
|
|
CVertexBuffer *m_pFlexVertexBuffer; |
|
|
|
bool m_bHasFlexVerts; |
|
int m_nFlexVertOffsetInBytes; |
|
int m_flexVertCount; |
|
|
|
// Primitive type |
|
MaterialPrimitiveType_t m_Type; |
|
|
|
// Primitive mode |
|
D3DPRIMITIVETYPE m_Mode; |
|
|
|
// Number of primitives |
|
unsigned int m_NumIndices; |
|
unsigned short m_NumVertices; |
|
|
|
// Is it locked? |
|
bool m_IsVBLocked; |
|
bool m_IsIBLocked; |
|
|
|
// Used in rendering sub-parts of the mesh |
|
static CPrimList *s_pPrims; |
|
static int s_nPrims; |
|
static unsigned int s_FirstVertex; // Gets reset during CMeshDX8::DrawInternal |
|
static unsigned int s_NumVertices; |
|
int m_FirstIndex; |
|
|
|
#ifdef RECORDING |
|
int m_LockVertexBufferSize; |
|
void *m_LockVertexBuffer; |
|
#endif |
|
|
|
#if defined( RECORDING ) || defined( CHECK_INDICES ) |
|
void *m_LockIndexBuffer; |
|
int m_LockIndexBufferSize; |
|
#endif |
|
const char *m_pTextureGroupName; |
|
|
|
friend class CMeshMgr; // MESHFIXME |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A little extra stuff for the dynamic version |
|
//----------------------------------------------------------------------------- |
|
class CDynamicMeshDX8 : public CMeshDX8 |
|
{ |
|
public: |
|
// constructor, destructor |
|
CDynamicMeshDX8(); |
|
virtual ~CDynamicMeshDX8(); |
|
|
|
// Initializes the dynamic mesh |
|
void Init( int nBufferId ); |
|
|
|
// Sets the vertex format |
|
virtual void SetVertexFormat( VertexFormat_t format ); |
|
|
|
// Resets the state in case of a task switch |
|
void Reset(); |
|
|
|
// Do I have enough room in the buffer? |
|
bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const; |
|
|
|
// returns the # of indices |
|
int IndexCount( ) const; |
|
|
|
// Locks the mesh |
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); |
|
|
|
// Unlocks the mesh |
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); |
|
|
|
// Override vertex + index buffer |
|
void OverrideVertexBuffer( CVertexBuffer *pStaticVertexBuffer ); |
|
void OverrideIndexBuffer( CIndexBuffer *pStaticIndexBuffer ); |
|
|
|
// Do I need to reset the vertex format? |
|
bool NeedsVertexFormatReset(VertexFormat_t fmt) const; |
|
|
|
// Draws it |
|
void Draw( int nFirstIndex, int nIndexCount ); |
|
void MarkAsDrawn() { m_HasDrawn = true; } |
|
// Simply draws what's been buffered up immediately, without state change |
|
void DrawSinglePassImmediately(); |
|
|
|
// Operation to do pre-lock |
|
void PreLock(); |
|
|
|
bool IsDynamic() const { return true; } |
|
|
|
private: |
|
// Resets buffering state |
|
void ResetVertexAndIndexCounts(); |
|
|
|
// Buffer Id |
|
int m_nBufferId; |
|
|
|
// total queued vertices |
|
int m_TotalVertices; |
|
int m_TotalIndices; |
|
|
|
// the first vertex and index since the last draw |
|
int m_nFirstVertex; |
|
int m_FirstIndex; |
|
|
|
// Have we drawn since the last lock? |
|
bool m_HasDrawn; |
|
|
|
// Any overrides? |
|
bool m_VertexOverride; |
|
bool m_IndexOverride; |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A mesh that stores temporary vertex data in the correct format (for modification) |
|
//----------------------------------------------------------------------------- |
|
class CTempMeshDX8 : public CBaseMeshDX8 |
|
{ |
|
public: |
|
// constructor, destructor |
|
CTempMeshDX8( bool isDynamic ); |
|
virtual ~CTempMeshDX8(); |
|
|
|
// Sets the material |
|
virtual void SetVertexFormat( VertexFormat_t format ); |
|
|
|
// Locks/unlocks the mesh |
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ); |
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ); |
|
|
|
// Locks mesh for modifying |
|
virtual void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); |
|
virtual void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ); |
|
virtual void ModifyEnd( MeshDesc_t& desc ); |
|
|
|
// Number of indices + vertices |
|
int VertexCount() const; |
|
virtual int IndexCount() const; |
|
virtual bool IsDynamic() const; |
|
|
|
// Sets the primitive type |
|
void SetPrimitiveType( MaterialPrimitiveType_t type ); |
|
MaterialPrimitiveType_t GetPrimitiveType() const; |
|
|
|
// Begins a pass |
|
void BeginPass( ); |
|
|
|
// Draws a single pass |
|
void RenderPass(); |
|
|
|
virtual void HandleLateCreation() |
|
{ |
|
Assert( !"TBD - CTempMeshDX8::HandleLateCreation()" ); |
|
} |
|
|
|
// Draws the entire beast |
|
void Draw( int nFirstIndex, int nIndexCount ); |
|
|
|
virtual void CopyToMeshBuilder( |
|
int iStartVert, // Which vertices to copy. |
|
int nVerts, |
|
int iStartIndex, // Which indices to copy. |
|
int nIndices, |
|
int indexOffset, // This is added to each index. |
|
CMeshBuilder &builder ); |
|
private: |
|
// Selection mode |
|
void TestSelection( ); |
|
void ClipTriangle( D3DXVECTOR3 **ppVert, float zNear, D3DXMATRIX &proj ); |
|
|
|
CDynamicMeshDX8 *GetDynamicMesh(); |
|
|
|
CUtlVector< unsigned char, CUtlMemoryAligned< unsigned char, 32 > > m_VertexData; |
|
CUtlVector< unsigned short > m_IndexData; |
|
|
|
unsigned short m_VertexSize; |
|
MaterialPrimitiveType_t m_Type; |
|
int m_LockedVerts; |
|
int m_LockedIndices; |
|
bool m_IsDynamic; |
|
|
|
// Used in rendering sub-parts of the mesh |
|
static unsigned int s_NumIndices; |
|
static unsigned int s_FirstIndex; |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
bool m_Locked; |
|
bool m_InPass; |
|
#endif |
|
}; |
|
|
|
#if 0 |
|
//----------------------------------------------------------------------------- |
|
// A mesh that stores temporary vertex data in the correct format (for modification) |
|
//----------------------------------------------------------------------------- |
|
class CTempIndexBufferDX8 : public CIndexBufferBase |
|
{ |
|
public: |
|
// constructor, destructor |
|
CTempIndexBufferDX8( bool isDynamic ); |
|
virtual ~CTempIndexBufferDX8(); |
|
|
|
// Locks/unlocks the mesh |
|
void LockIndexBuffer( int nIndexCount ); |
|
void UnlockMesh( int nIndexCount ); |
|
|
|
// Locks mesh for modifying |
|
virtual void ModifyBeginEx( bool bReadOnly, int nFirstIndex, int nIndexCount ); |
|
virtual void ModifyEnd(); |
|
|
|
// Number of indices |
|
virtual int IndexCount() const; |
|
virtual bool IsDynamic() const; |
|
|
|
virtual void CopyToIndexBuilder( |
|
int iStartIndex, // Which indices to copy. |
|
int nIndices, |
|
int indexOffset, // This is added to each index. |
|
CIndexBuilder &builder ); |
|
private: |
|
// Selection mode |
|
void TestSelection( ); |
|
|
|
CDynamicMeshDX8 *GetDynamicMesh(); |
|
|
|
CUtlVector< unsigned short > m_IndexData; |
|
|
|
MaterialPrimitiveType_t m_Type; |
|
int m_LockedIndices; |
|
bool m_IsDynamic; |
|
|
|
// Used in rendering sub-parts of the mesh |
|
static unsigned int s_NumIndices; |
|
static unsigned int s_FirstIndex; |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
bool m_Locked; |
|
bool m_InPass; |
|
#endif |
|
}; |
|
#endif |
|
//----------------------------------------------------------------------------- |
|
// This is a version that buffers up vertex data so we can blast through it later |
|
//----------------------------------------------------------------------------- |
|
class CBufferedMeshDX8 : public CBaseMeshDX8 |
|
{ |
|
public: |
|
// constructor, destructor |
|
CBufferedMeshDX8(); |
|
virtual ~CBufferedMeshDX8(); |
|
|
|
// checks to see if it was rendered.. |
|
void ResetRendered(); |
|
bool WasNotRendered() const; |
|
|
|
// Sets the mesh we're really going to draw into |
|
void SetMesh( CBaseMeshDX8* pMesh ); |
|
const CBaseMeshDX8* GetMesh() const { return m_pMesh; } |
|
|
|
// Spews the mesh data |
|
virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ); |
|
|
|
// Sets the vertex format |
|
virtual void SetVertexFormat( VertexFormat_t format ); |
|
virtual VertexFormat_t GetVertexFormat() const; |
|
|
|
// Sets the morph format |
|
virtual void SetMorphFormat( MorphFormat_t format ); |
|
|
|
// Sets the material |
|
void SetMaterial( IMaterial *pMaterial ); |
|
|
|
// returns the number of indices (should never be called!) |
|
virtual int IndexCount() const { Assert(0); return 0; } |
|
virtual MaterialIndexFormat_t IndexFormat() const { Assert(0); return MATERIAL_INDEX_FORMAT_16BIT; } |
|
virtual bool IsDynamic() const { Assert(0); return true; } |
|
virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); } |
|
virtual void EndCastBuffer( ) { Assert(0); } |
|
virtual int GetRoomRemaining() const { Assert(0); return 0; } |
|
|
|
// Locks the mesh |
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); |
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ); |
|
|
|
// Sets the primitive type |
|
void SetPrimitiveType( MaterialPrimitiveType_t type ); |
|
MaterialPrimitiveType_t GetPrimitiveType( ) const; |
|
|
|
void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & spewDesc ); |
|
|
|
virtual void HandleLateCreation( ) |
|
{ |
|
if ( m_pMesh ) |
|
{ |
|
m_pMesh->HandleLateCreation(); |
|
} |
|
} |
|
|
|
// Draws it |
|
void Draw( int nFirstIndex, int nIndexCount ); |
|
|
|
// Renders a pass |
|
void RenderPass(); |
|
|
|
// Flushes queued data |
|
void Flush( ); |
|
|
|
void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ); |
|
|
|
private: |
|
// The actual mesh we need to render.... |
|
CBaseMeshDX8* m_pMesh; |
|
|
|
// The index of the last vertex (for tristrip fixup) |
|
unsigned short m_LastIndex; |
|
|
|
// Extra padding indices for tristrips |
|
unsigned short m_ExtraIndices; |
|
|
|
// Am I currently flushing? |
|
bool m_IsFlushing; |
|
|
|
// has the dynamic mesh been rendered? |
|
bool m_WasRendered; |
|
|
|
// Do I need to flush? |
|
bool m_FlushNeeded; |
|
|
|
#ifdef DEBUG_BUFFERED_MESHES |
|
// for debugging only |
|
bool m_BufferedStateSet; |
|
BufferedState_t m_BufferedState; |
|
#endif |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Implementation of the mesh manager |
|
//----------------------------------------------------------------------------- |
|
class CMeshMgr : public IMeshMgr |
|
{ |
|
public: |
|
// constructor, destructor |
|
CMeshMgr(); |
|
virtual ~CMeshMgr(); |
|
|
|
// Initialize, shutdown |
|
void Init(); |
|
void Shutdown(); |
|
|
|
// Task switch... |
|
void ReleaseBuffers(); |
|
void RestoreBuffers(); |
|
|
|
// Releases all dynamic vertex buffers |
|
void DestroyVertexBuffers(); |
|
|
|
// Flushes the dynamic mesh |
|
void Flush(); |
|
|
|
// Flushes the vertex buffers |
|
void DiscardVertexBuffers(); |
|
|
|
// Creates, destroys static meshes |
|
IMesh *CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial *pMaterial = NULL ); |
|
void DestroyStaticMesh( IMesh *pMesh ); |
|
|
|
// Gets at the dynamic mesh (spoofs it though) |
|
IMesh *GetDynamicMesh( IMaterial *pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool buffered, |
|
IMesh *pVertexOverride, IMesh *pIndexOverride ); |
|
|
|
// ----------------------------------------------------------- |
|
// ------------ New Vertex/Index Buffer interface ---------------------------- |
|
// Do we need support for bForceTempMesh and bSoftwareVertexShader? |
|
// I don't think we use bSoftwareVertexShader anymore. .need to look into bForceTempMesh. |
|
IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ); |
|
IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ); |
|
void DestroyVertexBuffer( IVertexBuffer * ); |
|
void DestroyIndexBuffer( IIndexBuffer * ); |
|
// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? |
|
IVertexBuffer *GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered = true ); |
|
IIndexBuffer *GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered = true ); |
|
void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 ); |
|
void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ); |
|
void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ); |
|
// ------------ End ---------------------------- |
|
void RenderPassWithVertexAndIndexBuffers( void ); |
|
|
|
VertexFormat_t GetCurrentVertexFormat( void ) const { return m_CurrentVertexFormat; } |
|
|
|
// Gets at the *actual* dynamic mesh |
|
IMesh* GetActualDynamicMesh( VertexFormat_t vertexFormat ); |
|
IMesh *GetFlexMesh(); |
|
|
|
// Computes vertex format from a list of ingredients |
|
VertexFormat_t ComputeVertexFormat( unsigned int flags, |
|
int numTexCoords, int *pTexCoordDimensions, int numBoneWeights, |
|
int userDataSize ) const; |
|
|
|
// Use fat vertices (for tools) |
|
virtual void UseFatVertices( bool bUseFat ); |
|
|
|
// Returns the number of vertices we can render using the dynamic mesh |
|
virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ); |
|
virtual int GetMaxVerticesToRender( IMaterial *pMaterial ); |
|
virtual int GetMaxIndicesToRender( ); |
|
|
|
// Returns a vertex buffer appropriate for the flags |
|
CVertexBuffer *FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t fmt ); |
|
CIndexBuffer *GetDynamicIndexBuffer(); |
|
|
|
// Is the mesh dynamic? |
|
bool IsDynamicMesh( IMesh *pMesh ) const; |
|
bool IsBufferedDynamicMesh( IMesh *pMesh ) const; |
|
|
|
// Is the vertex buffer dynamic? |
|
bool IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const; |
|
|
|
// Is the index buffer dynamic? |
|
bool IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const; |
|
|
|
// Returns the vertex size |
|
int VertexFormatSize( VertexFormat_t vertexFormat ) const |
|
{ |
|
return CVertexBufferBase::VertexFormatSize( vertexFormat ); |
|
} |
|
|
|
// Computes the vertex buffer pointers |
|
void ComputeVertexDescription( unsigned char *pBuffer, |
|
VertexFormat_t vertexFormat, MeshDesc_t &desc ) const; |
|
|
|
// Returns the number of buffers... |
|
int BufferCount() const |
|
{ |
|
#ifdef _DEBUG |
|
return CVertexBuffer::BufferCount() + CIndexBuffer::BufferCount(); |
|
#else |
|
return 0; |
|
#endif |
|
} |
|
|
|
CVertexBuffer *GetVertexIDBuffer(); |
|
|
|
IVertexBuffer *GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered = true ); |
|
IIndexBuffer *GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered = true ); |
|
virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ); |
|
|
|
int UnusedVertexFields() const { return m_nUnusedVertexFields; } |
|
int UnusedTextureCoords() const { return m_nUnusedTextureCoords; } |
|
|
|
IDirect3DVertexBuffer9 *GetZeroVertexBuffer() const { return m_pZeroVertexBuffer; } |
|
|
|
private: |
|
void SetVertexIDStreamState( ); |
|
void SetColorStreamState( ); |
|
void SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride ); |
|
void SetIndexStreamState( int firstVertexIdx ); |
|
bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride ); |
|
|
|
struct VertexBufferLookup_t |
|
{ |
|
CVertexBuffer* m_pBuffer; |
|
int m_VertexSize; |
|
}; |
|
|
|
void CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, CMeshDX8 *pSrcIndexMesh ); |
|
|
|
// Cleans up the class |
|
void CleanUp(); |
|
|
|
// Creates, destroys the dynamic index |
|
void CreateDynamicIndexBuffer(); |
|
void DestroyDynamicIndexBuffer(); |
|
|
|
// Creates, destroys the vertexID buffer |
|
void CreateVertexIDBuffer(); |
|
void DestroyVertexIDBuffer(); |
|
void CreateZeroVertexBuffer(); |
|
void DestroyZeroVertexBuffer(); |
|
|
|
// Fills a vertexID buffer |
|
void FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount ); |
|
|
|
// The dynamic index buffer |
|
CIndexBuffer *m_pDynamicIndexBuffer; |
|
|
|
// A static vertexID buffer |
|
CVertexBuffer *m_pVertexIDBuffer; |
|
|
|
// The dynamic vertex buffers |
|
CUtlVector< VertexBufferLookup_t > m_DynamicVertexBuffers; |
|
|
|
// The buffered mesh |
|
CBufferedMeshDX8 m_BufferedMesh; |
|
|
|
// The current dynamic mesh |
|
CDynamicMeshDX8 m_DynamicMesh; |
|
CDynamicMeshDX8 m_DynamicFlexMesh; |
|
|
|
// The current dynamic vertex buffer |
|
CVertexBufferDx8 m_DynamicVertexBuffer; |
|
|
|
// The current dynamic index buffer |
|
CIndexBufferDx8 m_DynamicIndexBuffer; |
|
|
|
// The dynamic mesh temp version (for shaders that modify vertex data) |
|
CTempMeshDX8 m_DynamicTempMesh; |
|
|
|
// Am I buffering or not? |
|
bool m_BufferedMode; |
|
|
|
// Using fat vertices? |
|
bool m_bUseFatVertices; |
|
|
|
CVertexBufferDx8 *m_pCurrentVertexBuffer; |
|
VertexFormat_t m_CurrentVertexFormat; |
|
int m_pVertexBufferOffset[MAX_DX8_STREAMS]; |
|
int m_pCurrentVertexStride[MAX_DX8_STREAMS]; |
|
int m_pFirstVertex[MAX_DX8_STREAMS]; |
|
int m_pVertexCount[MAX_DX8_STREAMS]; |
|
CIndexBufferBase *m_pCurrentIndexBuffer; |
|
int m_nIndexBufferOffset; |
|
MaterialPrimitiveType_t m_PrimitiveType; |
|
int m_nFirstIndex; |
|
int m_nNumIndices; |
|
|
|
unsigned int m_nUnusedVertexFields; |
|
unsigned int m_nUnusedTextureCoords; |
|
|
|
// 4096 byte static VB containing all-zeros |
|
IDirect3DVertexBuffer9 *m_pZeroVertexBuffer; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Singleton... |
|
//----------------------------------------------------------------------------- |
|
static CMeshMgr g_MeshMgr; |
|
IMeshMgr* MeshMgr() |
|
{ |
|
return &g_MeshMgr; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Tracks stream state and queued data |
|
//----------------------------------------------------------------------------- |
|
static CIndexBuffer *g_pLastIndex = NULL; |
|
static IDirect3DIndexBuffer9 *g_pLastIndexBuffer = NULL; |
|
static CVertexBuffer *g_pLastVertex = NULL; |
|
static IDirect3DVertexBuffer9 *g_pLastVertexBuffer = NULL; |
|
static int g_nLastVertOffsetInBytes = 0; |
|
static int g_nLastVertStride = 0; |
|
static int g_LastVertexIdx = -1; |
|
static CMeshDX8 *g_pLastColorMesh = NULL; |
|
static int g_nLastColorMeshVertOffsetInBytes = 0; |
|
static bool g_bUsingVertexID = false; |
|
static bool g_bFlexMeshStreamSet = false; |
|
static VertexFormat_t g_LastVertexFormat = 0; |
|
|
|
inline void D3DSetStreamSource( unsigned int streamNumber, IDirect3DVertexBuffer9 *pStreamData, |
|
unsigned int nVertexOffsetInBytes, unsigned int stride ) |
|
{ |
|
Dx9Device()->SetStreamSource( streamNumber, pStreamData, nVertexOffsetInBytes, stride ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Tracks stream state and queued data |
|
//----------------------------------------------------------------------------- |
|
void Unbind( IDirect3DIndexBuffer9 *pIndexBuffer ) |
|
{ |
|
#ifdef _X360 |
|
IDirect3DIndexBuffer9 *pBoundBuffer; |
|
Dx9Device()->GetIndices( &pBoundBuffer ); |
|
if ( pBoundBuffer == pIndexBuffer ) |
|
{ |
|
// xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices |
|
Dx9Device()->SetIndices( NULL ); |
|
g_pLastIndex = NULL; |
|
g_pLastIndexBuffer = NULL; |
|
} |
|
|
|
if ( pBoundBuffer ) |
|
{ |
|
pBoundBuffer->Release(); |
|
} |
|
#endif |
|
} |
|
|
|
void Unbind( IDirect3DVertexBuffer9 *pVertexBuffer ) |
|
{ |
|
#ifdef _X360 |
|
UINT nOffset, nStride; |
|
IDirect3DVertexBuffer9 *pBoundBuffer; |
|
for ( int i = 0; i < MAX_DX8_STREAMS; ++i ) |
|
{ |
|
Dx9Device()->GetStreamSource( i, &pBoundBuffer, &nOffset, &nStride ); |
|
if ( pBoundBuffer == pVertexBuffer ) |
|
{ |
|
// xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices |
|
Dx9Device()->SetStreamSource( i, 0, 0, 0 ); |
|
switch ( i ) |
|
{ |
|
case 0: |
|
g_pLastVertex = NULL; |
|
g_pLastVertexBuffer = NULL; |
|
break; |
|
|
|
case 1: |
|
g_pLastColorMesh = NULL; |
|
g_nLastColorMeshVertOffsetInBytes = 0; |
|
break; |
|
} |
|
} |
|
|
|
if ( pBoundBuffer ) |
|
{ |
|
pBoundBuffer->Release(); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Helpers to count texture coordinates |
|
//----------------------------------------------------------------------------- |
|
static int NumTextureCoordinates( VertexFormat_t vertexFormat ) |
|
{ |
|
int nTexCoordCount = 0; |
|
for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) |
|
{ |
|
if ( TexCoordSize( i, vertexFormat ) == 0 ) |
|
continue; |
|
++nTexCoordCount; |
|
} |
|
return nTexCoordCount; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Makes sure that the render state is always set next time |
|
//----------------------------------------------------------------------------- |
|
static void ResetMeshRenderState() |
|
{ |
|
SafeRelease( &g_pLastIndex ); |
|
g_pLastIndexBuffer = 0; |
|
g_pLastVertex = 0; |
|
g_nLastVertOffsetInBytes = 0; |
|
g_pLastColorMesh = 0; |
|
g_nLastColorMeshVertOffsetInBytes = 0; |
|
g_LastVertexIdx = -1; |
|
g_bUsingVertexID = false; |
|
g_bFlexMeshStreamSet = false; |
|
g_LastVertexFormat = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Makes sure that the render state is always set next time |
|
//----------------------------------------------------------------------------- |
|
static void ResetIndexBufferRenderState() |
|
{ |
|
SafeRelease( &g_pLastIndex ); |
|
g_pLastIndexBuffer = 0; |
|
g_LastVertexIdx = -1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Index Buffer implementations begin here |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// Globals |
|
//----------------------------------------------------------------------------- |
|
#ifdef _DEBUG |
|
int CIndexBufferDx8::s_nBufferCount = 0; |
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CIndexBufferDx8::CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ) : |
|
BaseClass( pBudgetGroupName ) |
|
{ |
|
// Debugger(); |
|
|
|
Assert( nIndexCount != 0 ); |
|
|
|
// NOTE: MATERIAL_INDEX_FORMAT_UNKNOWN can't be dealt with under dx9 |
|
// because format is bound at buffer creation time. What we'll do |
|
// is just arbitrarily choose to use a 16-bit index buffer of the same size |
|
if ( fmt == MATERIAL_INDEX_FORMAT_UNKNOWN ) |
|
{ |
|
fmt = MATERIAL_INDEX_FORMAT_16BIT; |
|
nIndexCount /= 2; |
|
} |
|
|
|
m_pIndexBuffer = NULL; |
|
m_IndexFormat = fmt; |
|
m_nBufferSize = nIndexCount * IndexSize(); |
|
m_nIndexCount = nIndexCount; |
|
m_nFirstUnwrittenOffset = 0; |
|
m_bIsLocked = false; |
|
m_bIsDynamic = IsDynamicBufferType( bufferType ); |
|
m_bFlush = false; |
|
|
|
#ifdef CHECK_INDICES |
|
m_pShadowIndices = NULL; |
|
#endif |
|
|
|
#ifdef VPROF_ENABLED |
|
m_nVProfFrame = -1; |
|
#endif |
|
} |
|
|
|
CIndexBufferDx8::~CIndexBufferDx8() |
|
{ |
|
Free(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the index size |
|
//----------------------------------------------------------------------------- |
|
inline int CIndexBufferDx8::IndexSize() const |
|
{ |
|
Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN ); |
|
return ( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT ) ? 2 : 4; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates, destroys the index buffer |
|
//----------------------------------------------------------------------------- |
|
bool CIndexBufferDx8::Allocate() |
|
{ |
|
#ifdef OSX |
|
Debugger(); |
|
#endif |
|
Assert( !m_pIndexBuffer ); |
|
m_nFirstUnwrittenOffset = 0; |
|
|
|
// FIXME: This doesn't really work for dynamic buffers; dynamic buffers |
|
// can't have mixed-type indices in them. Bleah. |
|
D3DFORMAT format = ( m_IndexFormat == MATERIAL_INDEX_FORMAT_32BIT ) ? |
|
D3DFMT_INDEX32 : D3DFMT_INDEX16; |
|
|
|
DWORD usage = D3DUSAGE_WRITEONLY; |
|
if ( m_bIsDynamic ) |
|
{ |
|
usage |= D3DUSAGE_DYNAMIC; |
|
} |
|
|
|
HRESULT hr = Dx9Device()->CreateIndexBuffer( |
|
m_nBufferSize, usage, format, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL ); |
|
|
|
#if !defined( _X360 ) |
|
if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) ) |
|
{ |
|
// Don't have the memory for this. Try flushing all managed resources |
|
// out of vid mem and try again. |
|
// FIXME: need to record this |
|
Dx9Device()->EvictManagedResources(); |
|
hr = Dx9Device()->CreateIndexBuffer( |
|
m_nBufferSize, usage, format, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL ); |
|
} |
|
#endif // !X360 |
|
|
|
if ( FAILED(hr) || ( m_pIndexBuffer == NULL ) ) |
|
{ |
|
Warning( "CIndexBufferDx8::Allocate: CreateIndexBuffer failed!\n" ); |
|
return false; |
|
} |
|
|
|
if ( !m_bIsDynamic ) |
|
{ |
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, |
|
COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); |
|
} |
|
else |
|
{ |
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, |
|
COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize ); |
|
} |
|
|
|
#ifdef CHECK_INDICES |
|
Assert ( !m_pShadowIndices ); |
|
m_pShadowIndices = new unsigned char[ m_nBufferSize ]; |
|
memset( m_pShadowIndices, 0xFF, m_nBufferSize ); |
|
#endif // CHECK_INDICES |
|
|
|
#ifdef _DEBUG |
|
++s_nBufferCount; |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
void CIndexBufferDx8::Free() |
|
{ |
|
// FIXME: Unlock(0); |
|
if ( m_pIndexBuffer ) |
|
{ |
|
#ifdef _DEBUG |
|
--s_nBufferCount; |
|
#endif |
|
|
|
m_pIndexBuffer->Release(); |
|
m_pIndexBuffer = NULL; |
|
|
|
if ( !m_bIsDynamic ) |
|
{ |
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, |
|
COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); |
|
} |
|
else |
|
{ |
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER, |
|
COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize ); |
|
} |
|
} |
|
|
|
#ifdef CHECK_INDICES |
|
if ( m_pShadowIndices ) |
|
{ |
|
delete[] m_pShadowIndices; |
|
m_pShadowIndices = NULL; |
|
} |
|
#endif // CHECK_INDICES |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Index buffer information |
|
//----------------------------------------------------------------------------- |
|
int CIndexBufferDx8::IndexCount( ) const |
|
{ |
|
Assert( !m_bIsDynamic ); |
|
return m_nIndexCount; |
|
} |
|
|
|
MaterialIndexFormat_t CIndexBufferDx8::IndexFormat() const |
|
{ |
|
Assert( !m_bIsDynamic ); |
|
return m_IndexFormat; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns true if the buffer is dynamic |
|
//----------------------------------------------------------------------------- |
|
bool CIndexBufferDx8::IsDynamic() const |
|
{ |
|
return m_bIsDynamic; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Only used by dynamic buffers, indicates the next lock should perform a discard. |
|
//----------------------------------------------------------------------------- |
|
void CIndexBufferDx8::Flush() |
|
{ |
|
// This strange-looking line makes a flush only occur if the buffer is dynamic. |
|
m_bFlush = m_bIsDynamic; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the D3D buffer |
|
//----------------------------------------------------------------------------- |
|
IDirect3DIndexBuffer9* CIndexBufferDx8::GetDx9Buffer() |
|
{ |
|
return m_pIndexBuffer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns a shadowed index, for validation |
|
//----------------------------------------------------------------------------- |
|
#ifdef CHECK_INDICES |
|
unsigned short CIndexBufferDx8::GetShadowIndex( int i ) const |
|
{ |
|
Assert( i >= 0 && i < m_nIndexCount ); |
|
Assert( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT ); |
|
return *(unsigned short*)( &m_pShadowIndices[ i * IndexSize() ] ); |
|
} |
|
#endif // CHECK_INDICES |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to measure how much static buffer memory is touched each frame |
|
//----------------------------------------------------------------------------- |
|
void CIndexBufferDx8::HandlePerFrameTextureStats( int nFrame ) |
|
{ |
|
#ifdef VPROF_ENABLED |
|
if ( m_nVProfFrame != nFrame && !m_bIsDynamic ) |
|
{ |
|
m_nVProfFrame = nFrame; |
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_STATIC_INDEX_BUFFER, |
|
COUNTER_GROUP_TEXTURE_PER_FRAME, m_nBufferSize ); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Casts a dynamic buffer to be a particular index type |
|
//----------------------------------------------------------------------------- |
|
void CIndexBufferDx8::BeginCastBuffer( MaterialIndexFormat_t format ) |
|
{ |
|
// NOTE: This should have no effect under Dx9, since we can't recast index buffers. |
|
Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN ); |
|
Assert( m_bIsDynamic && ( m_IndexFormat == format ) ); |
|
} |
|
|
|
void CIndexBufferDx8::EndCastBuffer( ) |
|
{ |
|
// NOTE: This should have no effect under Dx9, since we can't recast index buffers. |
|
} |
|
|
|
int CIndexBufferDx8::GetRoomRemaining() const |
|
{ |
|
return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / IndexSize(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the index buffer |
|
//----------------------------------------------------------------------------- |
|
bool CIndexBufferDx8::Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc ) |
|
{ |
|
Assert( !m_bIsLocked && ( nMaxIndexCount != 0 ) && ( nMaxIndexCount <= m_nIndexCount ) ); |
|
Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN ); |
|
|
|
// FIXME: Why do we need to sync matrices now? |
|
ShaderUtil()->SyncMatrices(); |
|
g_ShaderMutex.Lock(); |
|
|
|
VPROF( "CIndexBufferX8::Lock" ); |
|
|
|
void *pLockedData = NULL; |
|
HRESULT hr; |
|
int nMemoryRequired; |
|
bool bHasEnoughMemory; |
|
UINT nLockFlags; |
|
|
|
// This can happen if the buffer was locked but a type wasn't bound |
|
if ( m_IndexFormat == MATERIAL_INDEX_FORMAT_UNKNOWN ) |
|
goto indexBufferLockFailed; |
|
|
|
// Just give the app crap buffers to fill up while we're suppressed... |
|
if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxIndexCount == 0 ) ) |
|
goto indexBufferLockFailed; |
|
|
|
// Did we ask for something too large? |
|
if ( nMaxIndexCount > m_nIndexCount ) |
|
{ |
|
Warning( "Too many indices for index buffer. . tell a programmer (%d>%d)\n", nMaxIndexCount, m_nIndexCount ); |
|
goto indexBufferLockFailed; |
|
} |
|
|
|
// We might not have a buffer owing to alt-tab type stuff |
|
if ( !m_pIndexBuffer ) |
|
{ |
|
if ( !Allocate() ) |
|
goto indexBufferLockFailed; |
|
} |
|
|
|
// Check to see if we have enough memory |
|
nMemoryRequired = nMaxIndexCount * IndexSize(); |
|
bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize ); |
|
|
|
nLockFlags = D3DLOCK_NOSYSLOCK; |
|
if ( bAppend ) |
|
{ |
|
// Can't have the first lock after a flush be an appending lock |
|
Assert( !m_bFlush ); |
|
|
|
// If we're appending and we don't have enough room, then puke! |
|
if ( !bHasEnoughMemory || m_bFlush ) |
|
goto indexBufferLockFailed; |
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; |
|
} |
|
else |
|
{ |
|
// If we're not appending, no overwrite unless we don't have enough room |
|
// If we're a static buffer, always discard if we're not appending |
|
if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic ) |
|
{ |
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; |
|
} |
|
else |
|
{ |
|
if ( m_bIsDynamic ) |
|
{ |
|
nLockFlags |= D3DLOCK_DISCARD; |
|
} |
|
m_nFirstUnwrittenOffset = 0; |
|
m_bFlush = false; |
|
} |
|
} |
|
|
|
#if !defined( _X360 ) |
|
hr = m_pIndexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); |
|
#else |
|
hr = m_pIndexBuffer->Lock( 0, 0, &pLockedData, nLockFlags ); |
|
pLockedData = ( ( unsigned char * )pLockedData + m_nFirstUnwrittenOffset ); |
|
#endif |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
Warning( "Failed to lock index buffer in CIndexBufferDx8::LockIndexBuffer\n" ); |
|
goto indexBufferLockFailed; |
|
} |
|
|
|
desc.m_pIndices = (unsigned short*)( pLockedData ); |
|
desc.m_nIndexSize = IndexSize() >> 1; |
|
if ( g_pHardwareConfig->SupportsStreamOffset() ) |
|
{ |
|
desc.m_nFirstIndex = 0; |
|
desc.m_nOffset = m_nFirstUnwrittenOffset; |
|
} |
|
else |
|
{ |
|
desc.m_nFirstIndex = m_nFirstUnwrittenOffset / IndexSize(); |
|
Assert( (int)( desc.m_nFirstIndex * IndexSize() ) == m_nFirstUnwrittenOffset ); |
|
desc.m_nOffset = 0; |
|
} |
|
m_bIsLocked = true; |
|
|
|
#ifdef CHECK_INDICES |
|
m_nLockIndexBufferSize = nMemoryRequired; |
|
m_pLockIndexBuffer = desc.m_pIndices; |
|
m_nLockIndexOffset = m_nFirstUnwrittenOffset; |
|
#endif // CHECK_INDICES |
|
|
|
return true; |
|
|
|
indexBufferLockFailed: |
|
g_ShaderMutex.Unlock(); |
|
|
|
// Set up a bogus index descriptor |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
desc.m_nFirstIndex = 0; |
|
desc.m_nOffset = 0; |
|
return false; |
|
} |
|
|
|
void CIndexBufferDx8::Unlock( int nWrittenIndexCount, IndexDesc_t &desc ) |
|
{ |
|
Assert( nWrittenIndexCount <= m_nIndexCount ); |
|
|
|
// NOTE: This can happen if another application finishes |
|
// initializing during the construction of a mesh |
|
if ( !m_bIsLocked ) |
|
return; |
|
|
|
#ifdef CHECK_INDICES |
|
memcpy( (unsigned char*)m_pShadowIndices + m_nLockIndexOffset, m_pLockIndexBuffer, nWrittenIndexCount * IndexSize() ); |
|
#endif // CHECK_INDICES |
|
|
|
if ( m_pIndexBuffer ) |
|
{ |
|
m_pIndexBuffer->Unlock(); |
|
} |
|
|
|
m_nFirstUnwrittenOffset += nWrittenIndexCount * IndexSize(); |
|
m_bIsLocked = false; |
|
g_ShaderMutex.Unlock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Vertex Buffer implementations begin here |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// globals |
|
//----------------------------------------------------------------------------- |
|
#ifdef _DEBUG |
|
int CVertexBufferDx8::s_nBufferCount = 0; |
|
#endif |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// constructor |
|
//----------------------------------------------------------------------------- |
|
CVertexBufferDx8::CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ) : |
|
BaseClass( pBudgetGroupName ) |
|
{ |
|
// Debugger(); |
|
Assert( nVertexCount != 0 ); |
|
|
|
m_pVertexBuffer = NULL; |
|
m_VertexFormat = fmt; |
|
m_nVertexCount = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? 0 : nVertexCount; |
|
m_nBufferSize = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? nVertexCount : nVertexCount * VertexSize(); |
|
m_nFirstUnwrittenOffset = 0; |
|
m_bIsLocked = false; |
|
m_bIsDynamic = ( type == SHADER_BUFFER_TYPE_DYNAMIC ) || ( type == SHADER_BUFFER_TYPE_DYNAMIC_TEMP ); |
|
m_bFlush = false; |
|
|
|
#ifdef VPROF_ENABLED |
|
if ( !m_bIsDynamic ) |
|
{ |
|
char name[256]; |
|
V_strcpy_safe( name, "TexGroup_global_" ); |
|
V_strcat_safe( name, pBudgetGroupName, sizeof(name) ); |
|
m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_GLOBAL ); |
|
|
|
V_strcpy_safe( name, "TexGroup_frame_" ); |
|
V_strcat_safe( name, pBudgetGroupName, sizeof(name) ); |
|
m_pFrameCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_PER_FRAME ); |
|
} |
|
else |
|
{ |
|
m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER, COUNTER_GROUP_TEXTURE_GLOBAL ); |
|
m_pFrameCounter = NULL; |
|
} |
|
m_nVProfFrame = -1; |
|
#endif |
|
} |
|
|
|
CVertexBufferDx8::~CVertexBufferDx8() |
|
{ |
|
Free(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the vertex size |
|
//----------------------------------------------------------------------------- |
|
inline int CVertexBufferDx8::VertexSize() const |
|
{ |
|
Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN ); |
|
return VertexFormatSize( m_VertexFormat ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates, destroys the vertex buffer |
|
//----------------------------------------------------------------------------- |
|
bool CVertexBufferDx8::Allocate() |
|
{ |
|
Assert( !m_pVertexBuffer ); |
|
m_nFirstUnwrittenOffset = 0; |
|
|
|
D3DPOOL pool = D3DPOOL_MANAGED; |
|
|
|
#if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) |
|
extern bool g_ShaderDeviceUsingD3D9Ex; |
|
if ( g_ShaderDeviceUsingD3D9Ex ) |
|
{ |
|
pool = D3DPOOL_DEFAULT; |
|
} |
|
#endif |
|
|
|
DWORD usage = D3DUSAGE_WRITEONLY; |
|
if ( m_bIsDynamic ) |
|
{ |
|
usage |= D3DUSAGE_DYNAMIC; |
|
pool = D3DPOOL_DEFAULT; |
|
// Dynamic meshes should never be compressed (slows down writing to them) |
|
Assert( CompressionType( GetVertexFormat() ) == VERTEX_COMPRESSION_NONE ); |
|
} |
|
|
|
HRESULT hr = Dx9Device()->CreateVertexBuffer( |
|
m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL ); |
|
|
|
#if !defined( _X360 ) |
|
if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) ) |
|
{ |
|
// Don't have the memory for this. Try flushing all managed resources |
|
// out of vid mem and try again. |
|
// FIXME: need to record this |
|
Dx9Device()->EvictManagedResources(); |
|
hr = Dx9Device()->CreateVertexBuffer( |
|
m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL ); |
|
} |
|
#endif // !X360 |
|
|
|
if ( FAILED(hr) || ( m_pVertexBuffer == NULL ) ) |
|
{ |
|
Warning( "CVertexBufferDx8::Allocate: CreateVertexBuffer failed!\n" ); |
|
return false; |
|
} |
|
|
|
// Track VB allocations |
|
g_VBAllocTracker->CountVB( m_pVertexBuffer, m_bIsDynamic, m_nBufferSize, VertexSize(), GetVertexFormat() ); |
|
|
|
#ifdef VPROF_ENABLED |
|
if ( IsX360() || !m_bIsDynamic ) |
|
{ |
|
Assert( m_pGlobalCounter ); |
|
*m_pGlobalCounter += m_nBufferSize; |
|
} |
|
#endif |
|
|
|
#ifdef _DEBUG |
|
++s_nBufferCount; |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
void CVertexBufferDx8::Free() |
|
{ |
|
// FIXME: Unlock(0); |
|
if ( m_pVertexBuffer ) |
|
{ |
|
#ifdef _DEBUG |
|
--s_nBufferCount; |
|
#endif |
|
|
|
// Track VB allocations |
|
g_VBAllocTracker->UnCountVB( m_pVertexBuffer ); |
|
|
|
#ifdef VPROF_ENABLED |
|
if ( IsX360() || !m_bIsDynamic ) |
|
{ |
|
Assert( m_pGlobalCounter ); |
|
*m_pGlobalCounter -= m_nBufferSize; |
|
} |
|
#endif |
|
|
|
m_pVertexBuffer->Release(); |
|
m_pVertexBuffer = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Vertex buffer information |
|
//----------------------------------------------------------------------------- |
|
int CVertexBufferDx8::VertexCount() const |
|
{ |
|
Assert( !m_bIsDynamic ); |
|
return m_nVertexCount; |
|
} |
|
|
|
VertexFormat_t CVertexBufferDx8::GetVertexFormat() const |
|
{ |
|
Assert( !m_bIsDynamic ); |
|
return m_VertexFormat; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns true if the buffer is dynamic |
|
//----------------------------------------------------------------------------- |
|
bool CVertexBufferDx8::IsDynamic() const |
|
{ |
|
return m_bIsDynamic; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Only used by dynamic buffers, indicates the next lock should perform a discard. |
|
//----------------------------------------------------------------------------- |
|
void CVertexBufferDx8::Flush() |
|
{ |
|
// This strange-looking line makes a flush only occur if the buffer is dynamic. |
|
m_bFlush = m_bIsDynamic; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the D3D buffer |
|
//----------------------------------------------------------------------------- |
|
IDirect3DVertexBuffer9* CVertexBufferDx8::GetDx9Buffer() |
|
{ |
|
return m_pVertexBuffer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Casts a dynamic buffer to be a particular vertex type |
|
//----------------------------------------------------------------------------- |
|
void CVertexBufferDx8::BeginCastBuffer( VertexFormat_t format ) |
|
{ |
|
Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN ); |
|
Assert( m_bIsDynamic && ( m_VertexFormat == 0 || m_VertexFormat == format ) ); |
|
if ( !m_bIsDynamic ) |
|
return; |
|
|
|
m_VertexFormat = format; |
|
int nVertexSize = VertexSize(); |
|
m_nVertexCount = m_nBufferSize / nVertexSize; |
|
|
|
// snap current position up to the next position based on expected size |
|
// so append can safely guarantee nooverwrite regardless of a format growth or shrinkage |
|
if ( !g_pHardwareConfig->SupportsStreamOffset() ) |
|
{ |
|
m_nFirstUnwrittenOffset = ( m_nFirstUnwrittenOffset + nVertexSize - 1 ) / nVertexSize; |
|
m_nFirstUnwrittenOffset *= nVertexSize; |
|
if ( m_nFirstUnwrittenOffset > m_nBufferSize ) |
|
{ |
|
m_nFirstUnwrittenOffset = m_nBufferSize; |
|
} |
|
} |
|
} |
|
|
|
void CVertexBufferDx8::EndCastBuffer( ) |
|
{ |
|
Assert( m_bIsDynamic && m_VertexFormat != 0 ); |
|
if ( !m_bIsDynamic ) |
|
return; |
|
m_VertexFormat = 0; |
|
m_nVertexCount = 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the number of vertices we can still write into the buffer |
|
//----------------------------------------------------------------------------- |
|
int CVertexBufferDx8::GetRoomRemaining() const |
|
{ |
|
return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / VertexSize(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the vertex buffer mesh |
|
//----------------------------------------------------------------------------- |
|
bool CVertexBufferDx8::Lock( int nMaxVertexCount, bool bAppend, VertexDesc_t &desc ) |
|
{ |
|
Assert( !m_bIsLocked && ( nMaxVertexCount != 0 ) && ( nMaxVertexCount <= m_nVertexCount ) ); |
|
Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN ); |
|
|
|
// FIXME: Why do we need to sync matrices now? |
|
ShaderUtil()->SyncMatrices(); |
|
g_ShaderMutex.Lock(); |
|
|
|
VPROF( "CVertexBufferDx8::Lock" ); |
|
|
|
void *pLockedData = NULL; |
|
HRESULT hr; |
|
int nMemoryRequired; |
|
bool bHasEnoughMemory; |
|
UINT nLockFlags; |
|
|
|
// This can happen if the buffer was locked but a type wasn't bound |
|
if ( m_VertexFormat == VERTEX_FORMAT_UNKNOWN ) |
|
goto vertexBufferLockFailed; |
|
|
|
// Just give the app crap buffers to fill up while we're suppressed... |
|
if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxVertexCount == 0 ) ) |
|
goto vertexBufferLockFailed; |
|
|
|
// Did we ask for something too large? |
|
if ( nMaxVertexCount > m_nVertexCount ) |
|
{ |
|
Warning( "Too many vertices for vertex buffer. . tell a programmer (%d>%d)\n", nMaxVertexCount, m_nVertexCount ); |
|
goto vertexBufferLockFailed; |
|
} |
|
|
|
// We might not have a buffer owing to alt-tab type stuff |
|
if ( !m_pVertexBuffer ) |
|
{ |
|
if ( !Allocate() ) |
|
goto vertexBufferLockFailed; |
|
} |
|
|
|
// Check to see if we have enough memory |
|
nMemoryRequired = nMaxVertexCount * VertexSize(); |
|
bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize ); |
|
|
|
nLockFlags = D3DLOCK_NOSYSLOCK; |
|
if ( bAppend ) |
|
{ |
|
// Can't have the first lock after a flush be an appending lock |
|
Assert( !m_bFlush ); |
|
|
|
// If we're appending and we don't have enough room, then puke! |
|
if ( !bHasEnoughMemory || m_bFlush ) |
|
goto vertexBufferLockFailed; |
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; |
|
} |
|
else |
|
{ |
|
// If we're not appending, no overwrite unless we don't have enough room |
|
// If we're a static buffer, always discard if we're not appending |
|
if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic ) |
|
{ |
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE; |
|
} |
|
else |
|
{ |
|
if ( m_bIsDynamic ) |
|
{ |
|
nLockFlags |= D3DLOCK_DISCARD; |
|
} |
|
m_nFirstUnwrittenOffset = 0; |
|
m_bFlush = false; |
|
} |
|
} |
|
|
|
#if !defined( _X360 ) |
|
hr = m_pVertexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags ); |
|
#else |
|
hr = m_pVertexBuffer->Lock( 0, 0, &pLockedData, nLockFlags ); |
|
pLockedData = (unsigned char*)pLockedData + m_nFirstUnwrittenOffset; |
|
#endif |
|
|
|
if ( FAILED( hr ) ) |
|
{ |
|
// Check if paged pool is in critical state ( < 5% free ) |
|
PAGED_POOL_INFO_t ppi; |
|
if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) && |
|
( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) ) |
|
{ |
|
Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); |
|
} |
|
else |
|
{ |
|
Warning( "Failed to lock vertex buffer in CVertexBufferDx8::Lock\n" ); |
|
} |
|
goto vertexBufferLockFailed; |
|
} |
|
|
|
ComputeVertexDescription( (unsigned char*)pLockedData, m_VertexFormat, desc ); |
|
if ( g_pHardwareConfig->SupportsStreamOffset() ) |
|
{ |
|
desc.m_nFirstVertex = 0; |
|
desc.m_nOffset = m_nFirstUnwrittenOffset; |
|
} |
|
else |
|
{ |
|
desc.m_nFirstVertex = m_nFirstUnwrittenOffset / VertexSize(); |
|
desc.m_nOffset = 0; |
|
Assert( m_nFirstUnwrittenOffset == VertexSize() * desc.m_nFirstVertex ); |
|
} |
|
m_bIsLocked = true; |
|
return true; |
|
|
|
vertexBufferLockFailed: |
|
ComputeVertexDescription( 0, 0, desc ); |
|
desc.m_nFirstVertex = 0; |
|
desc.m_nOffset = 0; |
|
return false; |
|
} |
|
|
|
|
|
void CVertexBufferDx8::Unlock( int nWrittenVertexCount, VertexDesc_t &desc ) |
|
{ |
|
Assert( nWrittenVertexCount <= m_nVertexCount ); |
|
|
|
// NOTE: This can happen if another application finishes |
|
// initializing during the construction of a mesh |
|
if ( !m_bIsLocked ) |
|
return; |
|
|
|
if ( m_pVertexBuffer ) |
|
{ |
|
m_pVertexBuffer->Unlock(); |
|
} |
|
|
|
m_nFirstUnwrittenOffset += nWrittenVertexCount * VertexSize(); |
|
m_bIsLocked = false; |
|
g_ShaderMutex.Unlock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to measure how much static buffer memory is touched each frame |
|
//----------------------------------------------------------------------------- |
|
void CVertexBufferDx8::HandlePerFrameTextureStats( int nFrame ) |
|
{ |
|
#ifdef VPROF_ENABLED |
|
if ( m_nVProfFrame != nFrame && !m_bIsDynamic ) |
|
{ |
|
m_nVProfFrame = nFrame; |
|
m_pFrameCounter += m_nBufferSize; |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Helpers with meshdescs... |
|
//----------------------------------------------------------------------------- |
|
// FIXME: add compression-agnostic read-accessors (which decompress and return by value, checking desc.m_CompressionType) |
|
inline D3DXVECTOR3 &Position( MeshDesc_t const &desc, int vert ) |
|
{ |
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pPosition + vert * desc.m_VertexSize_Position ); |
|
} |
|
|
|
inline float Wrinkle( MeshDesc_t const &desc, int vert ) |
|
{ |
|
return *(float*)((unsigned char*)desc.m_pWrinkle + vert * desc.m_VertexSize_Wrinkle ); |
|
} |
|
|
|
inline D3DXVECTOR3 &BoneWeight( MeshDesc_t const &desc, int vert ) |
|
{ |
|
Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); |
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pBoneWeight + vert * desc.m_VertexSize_BoneWeight ); |
|
} |
|
|
|
inline unsigned char *BoneIndex( MeshDesc_t const &desc, int vert ) |
|
{ |
|
return desc.m_pBoneMatrixIndex + vert * desc.m_VertexSize_BoneMatrixIndex; |
|
} |
|
|
|
inline D3DXVECTOR3 &Normal( MeshDesc_t const &desc, int vert ) |
|
{ |
|
Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE ); |
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pNormal + vert * desc.m_VertexSize_Normal ); |
|
} |
|
|
|
inline unsigned char *Color( MeshDesc_t const &desc, int vert ) |
|
{ |
|
return desc.m_pColor + vert * desc.m_VertexSize_Color; |
|
} |
|
|
|
inline D3DXVECTOR2 &TexCoord( MeshDesc_t const &desc, int vert, int stage ) |
|
{ |
|
return *(D3DXVECTOR2*)((unsigned char*)desc.m_pTexCoord[stage] + vert * desc.m_VertexSize_TexCoord[stage] ); |
|
} |
|
|
|
inline D3DXVECTOR3 &TangentS( MeshDesc_t const &desc, int vert ) |
|
{ |
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentS + vert * desc.m_VertexSize_TangentS ); |
|
} |
|
|
|
inline D3DXVECTOR3 &TangentT( MeshDesc_t const &desc, int vert ) |
|
{ |
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentT + vert * desc.m_VertexSize_TangentT ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Base mesh |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
|
|
CBaseMeshDX8::CBaseMeshDX8() : m_VertexFormat(0) |
|
{ |
|
m_bMeshLocked = false; |
|
#ifdef DBGFLAG_ASSERT |
|
m_IsDrawing = false; |
|
m_pMaterial = 0; |
|
#endif |
|
} |
|
|
|
CBaseMeshDX8::~CBaseMeshDX8() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// For debugging... |
|
//----------------------------------------------------------------------------- |
|
bool CBaseMeshDX8::DebugTrace() const |
|
{ |
|
#ifdef _DEBUG |
|
if (m_pMaterial) |
|
return m_pMaterial->PerformDebugTrace(); |
|
#endif |
|
|
|
return false; |
|
} |
|
|
|
void CBaseMeshDX8::SetMaterial( IMaterial *pMaterial ) |
|
{ |
|
#ifdef DBGFLAG_ASSERT |
|
m_pMaterial = static_cast<IMaterialInternal *>(pMaterial); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets, gets the vertex format |
|
//----------------------------------------------------------------------------- |
|
void CBaseMeshDX8::SetVertexFormat( VertexFormat_t format ) |
|
{ |
|
m_VertexFormat = format; |
|
} |
|
|
|
VertexFormat_t CBaseMeshDX8::GetVertexFormat() const |
|
{ |
|
return m_VertexFormat; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets/gets the morph format |
|
//----------------------------------------------------------------------------- |
|
void CBaseMeshDX8::SetMorphFormat( MorphFormat_t format ) |
|
{ |
|
m_MorphFormat = format; |
|
} |
|
|
|
MorphFormat_t CBaseMeshDX8::GetMorphFormat() const |
|
{ |
|
return m_MorphFormat; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Am I using morph data? |
|
//----------------------------------------------------------------------------- |
|
bool CBaseMeshDX8::IsUsingMorphData() const |
|
{ |
|
LOCK_SHADERAPI(); |
|
// We're not using a morph unless the bound morph is a superset of what the rendermesh needs |
|
MorphFormat_t morphFormat = GetMorphFormat(); |
|
if ( !morphFormat ) |
|
return false; |
|
|
|
return ( ( morphFormat & ShaderUtil()->GetBoundMorphFormat() ) == morphFormat ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Do I need to reset the vertex format? |
|
//----------------------------------------------------------------------------- |
|
bool CBaseMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const |
|
{ |
|
return m_VertexFormat != fmt; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Do I have enough room? |
|
//----------------------------------------------------------------------------- |
|
bool CBaseMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const |
|
{ |
|
// by default, we do |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Estimate the memory used |
|
//----------------------------------------------------------------------------- |
|
unsigned CBaseMeshDX8::ComputeMemoryUsed() |
|
{ |
|
unsigned size = 0; |
|
|
|
if ( GetVertexBuffer() ) |
|
{ |
|
size += GetVertexBuffer()->VertexCount() * GetVertexBuffer()->VertexSize(); |
|
} |
|
|
|
if ( GetIndexBuffer() ) |
|
{ |
|
size += GetIndexBuffer()->IndexCount() * GetIndexBuffer()->IndexSize(); |
|
} |
|
|
|
return size; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks mesh for modifying |
|
//----------------------------------------------------------------------------- |
|
void CBaseMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
// for the time being, disallow for most cases |
|
Assert(0); |
|
} |
|
|
|
void CBaseMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
// for the time being, disallow for most cases |
|
Assert(0); |
|
} |
|
|
|
void CBaseMeshDX8::ModifyEnd( MeshDesc_t& desc ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
// for the time being, disallow for most cases |
|
Assert(0); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Begins a pass |
|
//----------------------------------------------------------------------------- |
|
void CBaseMeshDX8::BeginPass( ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the render state and gets the drawing going |
|
//----------------------------------------------------------------------------- |
|
inline void CBaseMeshDX8::DrawMesh( ) |
|
{ |
|
#ifdef DBGFLAG_ASSERT |
|
// Make sure we're not drawing... |
|
Assert( !m_IsDrawing ); |
|
m_IsDrawing = true; |
|
#endif |
|
|
|
// This is going to cause RenderPass to get called a bunch |
|
ShaderAPI()->DrawMesh( this ); |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
m_IsDrawing = false; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Spews the mesh data |
|
//----------------------------------------------------------------------------- |
|
void CBaseMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
// This has regressed. |
|
int i; |
|
|
|
|
|
// FIXME: just fall back to the base class (CVertexBufferBase) version of this function! |
|
|
|
|
|
#ifdef _DEBUG |
|
if( m_pMaterial ) |
|
{ |
|
Plat_DebugString( ( const char * )m_pMaterial->GetName() ); |
|
Plat_DebugString( "\n" ); |
|
} |
|
#endif // _DEBUG |
|
|
|
// This is needed so buffering can just use this |
|
VertexFormat_t fmt = m_VertexFormat; |
|
|
|
// Set up the vertex descriptor |
|
MeshDesc_t desc = spewDesc; |
|
|
|
char tempbuf[256]; |
|
char* temp = tempbuf; |
|
sprintf( tempbuf,"\nVerts: (Vertex Format %llx)\n", fmt); |
|
Plat_DebugString(tempbuf); |
|
|
|
CVertexBufferBase::PrintVertexFormat( fmt ); |
|
|
|
int numBoneWeights = NumBoneWeights( fmt ); |
|
for ( i = 0; i < nVertexCount; ++i ) |
|
{ |
|
temp += sprintf( temp, "[%4d] ", i + desc.m_nFirstVertex ); |
|
if( fmt & VERTEX_POSITION ) |
|
{ |
|
D3DXVECTOR3& pos = Position( desc, i ); |
|
temp += sprintf(temp, "P %8.2f %8.2f %8.2f ", |
|
pos[0], pos[1], pos[2]); |
|
} |
|
|
|
if ( fmt & VERTEX_WRINKLE ) |
|
{ |
|
float flWrinkle = Wrinkle( desc, i ); |
|
temp += sprintf(temp, "Wr %8.2f ",flWrinkle ); |
|
} |
|
|
|
if (numBoneWeights > 0) |
|
{ |
|
temp += sprintf(temp, "BW "); |
|
float* pWeight = BoneWeight( desc, i ); |
|
for (int j = 0; j < numBoneWeights; ++j) |
|
{ |
|
temp += sprintf(temp, "%1.2f ", pWeight[j]); |
|
} |
|
} |
|
if ( fmt & VERTEX_BONE_INDEX ) |
|
{ |
|
unsigned char *pIndex = BoneIndex( desc, i ); |
|
temp += sprintf( temp, "BI %d %d %d %d ", ( int )pIndex[0], ( int )pIndex[1], ( int )pIndex[2], ( int )pIndex[3] ); |
|
Assert( pIndex[0] < 16 ); |
|
Assert( pIndex[1] < 16 ); |
|
Assert( pIndex[2] < 16 ); |
|
Assert( pIndex[3] < 16 ); |
|
} |
|
|
|
if ( fmt & VERTEX_NORMAL ) |
|
{ |
|
D3DXVECTOR3& normal = Normal( desc, i ); |
|
temp += sprintf(temp, "N %1.2f %1.2f %1.2f ", |
|
normal[0], normal[1], normal[2]); |
|
} |
|
|
|
if (fmt & VERTEX_COLOR) |
|
{ |
|
unsigned char* pColor = Color( desc, i ); |
|
temp += sprintf(temp, "C b %3d g %3d r %3d a %3d ", |
|
pColor[0], pColor[1], pColor[2], pColor[3]); |
|
} |
|
|
|
for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j) |
|
{ |
|
if( TexCoordSize( j, fmt ) > 0) |
|
{ |
|
D3DXVECTOR2& texcoord = TexCoord( desc, i, j ); |
|
temp += sprintf(temp, "T%d %.2f %.2f ", j,texcoord[0], texcoord[1]); |
|
} |
|
} |
|
|
|
if (fmt & VERTEX_TANGENT_S) |
|
{ |
|
D3DXVECTOR3& tangentS = TangentS( desc, i ); |
|
temp += sprintf(temp, "S %1.2f %1.2f %1.2f ", |
|
tangentS[0], tangentS[1], tangentS[2]); |
|
} |
|
|
|
if (fmt & VERTEX_TANGENT_T) |
|
{ |
|
D3DXVECTOR3& tangentT = TangentT( desc, i ); |
|
temp += sprintf(temp, "T %1.2f %1.2f %1.2f ", |
|
tangentT[0], tangentT[1], tangentT[2]); |
|
} |
|
|
|
sprintf(temp,"\n"); |
|
Plat_DebugString(tempbuf); |
|
temp = tempbuf; |
|
} |
|
|
|
sprintf( tempbuf,"\nIndices: %d\n", nIndexCount ); |
|
Plat_DebugString(tempbuf); |
|
for ( i = 0; i < nIndexCount; ++i ) |
|
{ |
|
temp += sprintf( temp, "%d ", ( int )desc.m_pIndices[i] ); |
|
if ((i & 0x0F) == 0x0F) |
|
{ |
|
sprintf( temp, "\n" ); |
|
Plat_DebugString(tempbuf); |
|
tempbuf[0] = '\0'; |
|
temp = tempbuf; |
|
} |
|
} |
|
sprintf(temp,"\n"); |
|
Plat_DebugString( tempbuf ); |
|
} |
|
|
|
void CBaseMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
#ifdef VALIDATE_DEBUG |
|
int i; |
|
|
|
|
|
// FIXME: just fall back to the base class (CVertexBufferBase) version of this function! |
|
|
|
|
|
// This is needed so buffering can just use this |
|
VertexFormat_t fmt = m_pMaterial->GetVertexUsage(); |
|
|
|
// Set up the vertex descriptor |
|
MeshDesc_t desc = spewDesc; |
|
|
|
int numBoneWeights = NumBoneWeights( fmt ); |
|
for ( i = 0; i < nVertexCount; ++i ) |
|
{ |
|
if( fmt & VERTEX_POSITION ) |
|
{ |
|
D3DXVECTOR3& pos = Position( desc, i ); |
|
Assert( IsFinite( pos[0] ) && IsFinite( pos[1] ) && IsFinite( pos[2] ) ); |
|
} |
|
if( fmt & VERTEX_WRINKLE ) |
|
{ |
|
float flWrinkle = Wrinkle( desc, i ); |
|
Assert( IsFinite( flWrinkle ) ); |
|
} |
|
if (numBoneWeights > 0) |
|
{ |
|
float* pWeight = BoneWeight( desc, i ); |
|
for (int j = 0; j < numBoneWeights; ++j) |
|
{ |
|
Assert( pWeight[j] >= 0.0f && pWeight[j] <= 1.0f ); |
|
} |
|
} |
|
if( fmt & VERTEX_BONE_INDEX ) |
|
{ |
|
unsigned char *pIndex = BoneIndex( desc, i ); |
|
Assert( pIndex[0] >= 0 && pIndex[0] < 16 ); |
|
Assert( pIndex[1] >= 0 && pIndex[1] < 16 ); |
|
Assert( pIndex[2] >= 0 && pIndex[2] < 16 ); |
|
Assert( pIndex[3] >= 0 && pIndex[3] < 16 ); |
|
} |
|
if( fmt & VERTEX_NORMAL ) |
|
{ |
|
D3DXVECTOR3& normal = Normal( desc, i ); |
|
Assert( normal[0] >= -1.05f && normal[0] <= 1.05f ); |
|
Assert( normal[1] >= -1.05f && normal[1] <= 1.05f ); |
|
Assert( normal[2] >= -1.05f && normal[2] <= 1.05f ); |
|
} |
|
|
|
if (fmt & VERTEX_COLOR) |
|
{ |
|
int* pColor = (int*)Color( desc, i ); |
|
Assert( *pColor != FLOAT32_NAN_BITS ); |
|
} |
|
|
|
for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j) |
|
{ |
|
if( TexCoordSize( j, fmt ) > 0) |
|
{ |
|
D3DXVECTOR2& texcoord = TexCoord( desc, i, j ); |
|
Assert( IsFinite( texcoord[0] ) && IsFinite( texcoord[1] ) ); |
|
} |
|
} |
|
|
|
if (fmt & VERTEX_TANGENT_S) |
|
{ |
|
D3DXVECTOR3& tangentS = TangentS( desc, i ); |
|
Assert( IsFinite( tangentS[0] ) && IsFinite( tangentS[1] ) && IsFinite( tangentS[2] ) ); |
|
} |
|
|
|
if (fmt & VERTEX_TANGENT_T) |
|
{ |
|
D3DXVECTOR3& tangentT = TangentT( desc, i ); |
|
Assert( IsFinite( tangentT[0] ) && IsFinite( tangentT[1] ) && IsFinite( tangentT[2] ) ); |
|
} |
|
} |
|
#endif // _DEBUG |
|
} |
|
|
|
void CBaseMeshDX8::Draw( CPrimList *pLists, int nLists ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
Assert( !"CBaseMeshDX8::Draw(CPrimList, int): should never get here." ); |
|
} |
|
|
|
|
|
// Copy verts and/or indices to a mesh builder. This only works for temp meshes! |
|
void CBaseMeshDX8::CopyToMeshBuilder( |
|
int iStartVert, // Which vertices to copy. |
|
int nVerts, |
|
int iStartIndex, // Which indices to copy. |
|
int nIndices, |
|
int indexOffset, // This is added to each index. |
|
CMeshBuilder &builder ) |
|
{ |
|
LOCK_SHADERAPI(); |
|
Assert( false ); |
|
Warning( "CopyToMeshBuilder called on something other than a temp mesh.\n" ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// static mesh |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
CPrimList *CMeshDX8::s_pPrims; |
|
int CMeshDX8::s_nPrims; |
|
unsigned int CMeshDX8::s_FirstVertex; |
|
unsigned int CMeshDX8::s_NumVertices; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes the mode |
|
//----------------------------------------------------------------------------- |
|
inline D3DPRIMITIVETYPE ComputeMode( MaterialPrimitiveType_t type ) |
|
{ |
|
switch(type) |
|
{ |
|
#ifdef _X360 |
|
case MATERIAL_INSTANCED_QUADS: |
|
return D3DPT_QUADLIST; |
|
#endif |
|
|
|
case MATERIAL_POINTS: |
|
return D3DPT_POINTLIST; |
|
|
|
case MATERIAL_LINES: |
|
return D3DPT_LINELIST; |
|
|
|
case MATERIAL_TRIANGLES: |
|
return D3DPT_TRIANGLELIST; |
|
|
|
case MATERIAL_TRIANGLE_STRIP: |
|
return D3DPT_TRIANGLESTRIP; |
|
|
|
// Here, we expect to have the type set later. only works for static meshes |
|
case MATERIAL_HETEROGENOUS: |
|
return (D3DPRIMITIVETYPE)-1; |
|
|
|
default: |
|
Assert(0); |
|
return (D3DPRIMITIVETYPE)-1; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// constructor |
|
//----------------------------------------------------------------------------- |
|
CMeshDX8::CMeshDX8( const char *pTextureGroupName ) : m_NumVertices(0), m_NumIndices(0), m_pVertexBuffer(0), |
|
m_pColorMesh( 0 ), m_nColorMeshVertOffsetInBytes( 0 ), |
|
m_pIndexBuffer(0), m_Type(MATERIAL_TRIANGLES), m_IsVBLocked(false), |
|
m_IsIBLocked(false) |
|
{ |
|
m_pTextureGroupName = pTextureGroupName; |
|
m_Mode = ComputeMode(m_Type); |
|
|
|
m_bHasFlexVerts = false; |
|
m_pFlexVertexBuffer = NULL; |
|
m_nFlexVertOffsetInBytes = 0; |
|
} |
|
|
|
CMeshDX8::~CMeshDX8() |
|
{ |
|
// Don't release the vertex buffer |
|
if (!g_MeshMgr.IsDynamicMesh(this)) |
|
{ |
|
if (m_pVertexBuffer) |
|
{ |
|
delete m_pVertexBuffer; |
|
} |
|
if (m_pIndexBuffer) |
|
{ |
|
SafeRelease( &m_pIndexBuffer ); |
|
} |
|
} |
|
} |
|
|
|
void CMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ) |
|
{ |
|
if ( !ShaderUtil()->OnSetFlexMesh( this, pMesh, nVertexOffsetInBytes ) ) |
|
return; |
|
|
|
LOCK_SHADERAPI(); |
|
m_nFlexVertOffsetInBytes = nVertexOffsetInBytes; // Offset into dynamic mesh (in bytes) |
|
|
|
if ( pMesh ) |
|
{ |
|
m_flexVertCount = pMesh->VertexCount(); |
|
pMesh->MarkAsDrawn(); |
|
|
|
CBaseMeshDX8 *pBaseMesh = static_cast<CBaseMeshDX8 *>(pMesh); |
|
m_pFlexVertexBuffer = pBaseMesh->GetVertexBuffer(); |
|
|
|
m_bHasFlexVerts = true; |
|
} |
|
else |
|
{ |
|
m_flexVertCount = 0; |
|
m_pFlexVertexBuffer = NULL; |
|
m_bHasFlexVerts = false; |
|
} |
|
} |
|
|
|
void CMeshDX8::DisableFlexMesh( ) |
|
{ |
|
CMeshDX8::SetFlexMesh( NULL, 0 ); |
|
} |
|
|
|
bool CMeshDX8::HasFlexMesh( ) const |
|
{ |
|
LOCK_SHADERAPI(); |
|
return m_bHasFlexVerts; |
|
} |
|
|
|
void CMeshDX8::SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes ) |
|
{ |
|
if ( !ShaderUtil()->OnSetColorMesh( this, pColorMesh, nVertexOffsetInBytes ) ) |
|
return; |
|
|
|
LOCK_SHADERAPI(); |
|
m_pColorMesh = ( CMeshDX8 * )pColorMesh; // dangerous conversion! garymcthack |
|
m_nColorMeshVertOffsetInBytes = nVertexOffsetInBytes; |
|
Assert( m_pColorMesh || ( nVertexOffsetInBytes == 0 ) ); |
|
|
|
#ifdef _DEBUG |
|
if ( pColorMesh ) |
|
{ |
|
int nVertexCount = VertexCount(); |
|
int numVertsColorMesh = m_pColorMesh->VertexCount(); |
|
Assert( numVertsColorMesh >= nVertexCount ); |
|
} |
|
#endif |
|
} |
|
|
|
|
|
void CMeshDX8::HandleLateCreation( ) |
|
{ |
|
if ( m_pVertexBuffer ) |
|
{ |
|
m_pVertexBuffer->HandleLateCreation(); |
|
} |
|
if ( m_pIndexBuffer ) |
|
{ |
|
m_pIndexBuffer->HandleLateCreation(); |
|
} |
|
if ( m_pFlexVertexBuffer ) |
|
{ |
|
m_pFlexVertexBuffer->HandleLateCreation(); |
|
} |
|
|
|
if ( m_pColorMesh ) |
|
{ |
|
m_pColorMesh->HandleLateCreation(); |
|
} |
|
} |
|
|
|
|
|
bool CMeshDX8::HasColorMesh( ) const |
|
{ |
|
LOCK_SHADERAPI(); |
|
return (m_pColorMesh != NULL); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/ unlocks the vertex buffer |
|
//----------------------------------------------------------------------------- |
|
bool CMeshDX8::Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) |
|
{ |
|
Assert( !m_IsVBLocked ); |
|
|
|
// Just give the app crap buffers to fill up while we're suppressed... |
|
if ( g_pShaderDeviceDx8->IsDeactivated() || (nVertexCount == 0)) |
|
{ |
|
// Set up the vertex descriptor |
|
CVertexBufferBase::ComputeVertexDescription( 0, 0, desc ); |
|
desc.m_nFirstVertex = 0; |
|
return false; |
|
} |
|
|
|
// Static vertex buffer case |
|
if (!m_pVertexBuffer) |
|
{ |
|
int size = g_MeshMgr.VertexFormatSize( m_VertexFormat ); |
|
m_pVertexBuffer = new CVertexBuffer( Dx9Device(), m_VertexFormat, 0, size, nVertexCount, m_pTextureGroupName, ShaderAPI()->UsingSoftwareVertexProcessing() ); |
|
} |
|
|
|
// Lock it baby |
|
int nMaxVerts, nMaxIndices; |
|
g_MeshMgr.GetMaxToRender( this, false, &nMaxVerts, &nMaxIndices ); |
|
if ( !g_pHardwareConfig->SupportsStreamOffset() ) |
|
{ |
|
// Without stream offset, we can't use VBs greater than 65535 verts (due to our using 16-bit indices) |
|
Assert( nVertexCount <= nMaxVerts ); |
|
} |
|
|
|
unsigned char *pVertexMemory = m_pVertexBuffer->Lock( nVertexCount, desc.m_nFirstVertex ); |
|
if ( !pVertexMemory ) |
|
{ |
|
if ( nVertexCount > nMaxVerts ) |
|
{ |
|
Assert( 0 ); |
|
Error( "Too many verts for a dynamic vertex buffer (%d>%d) Tell a programmer to up VERTEX_BUFFER_SIZE.\n", |
|
( int )nVertexCount, ( int )nMaxVerts ); |
|
} |
|
else |
|
{ |
|
// Check if paged pool is in critical state ( < 5% free ) |
|
PAGED_POOL_INFO_t ppi; |
|
if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) && |
|
( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) ) |
|
{ |
|
Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); |
|
} |
|
else |
|
{ |
|
Assert( 0 ); |
|
Error( "failed to lock vertex buffer in CMeshDX8::LockVertexBuffer: nVertexCount=%d, nFirstVertex=%d\n", nVertexCount, desc.m_nFirstVertex ); |
|
} |
|
} |
|
CVertexBufferBase::ComputeVertexDescription( 0, 0, desc ); |
|
return false; |
|
} |
|
|
|
// Set up the vertex descriptor |
|
CVertexBufferBase::ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); |
|
m_IsVBLocked = true; |
|
|
|
#ifdef RECORDING |
|
m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize; |
|
m_LockVertexBuffer = pVertexMemory; |
|
#endif |
|
|
|
return true; |
|
} |
|
|
|
void CMeshDX8::Unlock( int nVertexCount, VertexDesc_t& desc ) |
|
{ |
|
// NOTE: This can happen if another application finishes |
|
// initializing during the construction of a mesh |
|
if (!m_IsVBLocked) |
|
return; |
|
|
|
// This is recorded for debugging. . not sent to dx. |
|
RECORD_COMMAND( DX8_SET_VERTEX_BUFFER_FORMAT, 2 ); |
|
RECORD_INT( m_pVertexBuffer->UID() ); |
|
RECORD_INT( m_VertexFormat ); |
|
|
|
RECORD_COMMAND( DX8_VERTEX_DATA, 3 ); |
|
RECORD_INT( m_pVertexBuffer->UID() ); |
|
RECORD_INT( m_LockVertexBufferSize ); |
|
RECORD_STRUCT( m_LockVertexBuffer, m_LockVertexBufferSize ); |
|
|
|
Assert(m_pVertexBuffer); |
|
m_pVertexBuffer->Unlock(nVertexCount); |
|
m_IsVBLocked = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the index buffer |
|
//----------------------------------------------------------------------------- |
|
int CMeshDX8::Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &desc ) |
|
{ |
|
Assert( !m_IsIBLocked ); |
|
|
|
// Just give the app crap buffers to fill up while we're suppressed... |
|
if ( g_pShaderDeviceDx8->IsDeactivated() || (nIndexCount == 0)) |
|
{ |
|
// Set up a bogus index descriptor |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
return 0; |
|
} |
|
|
|
// Static vertex buffer case |
|
if (!m_pIndexBuffer) |
|
{ |
|
SafeAssign( &m_pIndexBuffer, new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing() ) ); |
|
} |
|
|
|
int startIndex; |
|
desc.m_pIndices = m_pIndexBuffer->Lock( bReadOnly, nIndexCount, startIndex, nFirstIndex ); |
|
if( !desc.m_pIndices ) |
|
{ |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
|
|
// Check if paged pool is in critical state ( < 5% free ) |
|
PAGED_POOL_INFO_t ppi; |
|
if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) && |
|
( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) ) |
|
{ |
|
Error( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com/cgi-bin/steampowered.cfg/php/enduser/std_adp.php?p_faqid=150\n" ); |
|
} |
|
else |
|
{ |
|
Assert( 0 ); |
|
Error( "failed to lock index buffer in CMeshDX8::LockIndexBuffer\n" ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
desc.m_nIndexSize = 1; |
|
m_IsIBLocked = true; |
|
|
|
#if defined( RECORDING ) || defined( CHECK_INDICES ) |
|
m_LockIndexBufferSize = nIndexCount * 2; |
|
m_LockIndexBuffer = desc.m_pIndices; |
|
#endif |
|
|
|
return startIndex; |
|
} |
|
|
|
|
|
void CMeshDX8::Unlock( int nIndexCount, IndexDesc_t &desc ) |
|
{ |
|
// NOTE: This can happen if another application finishes |
|
// initializing during the construction of a mesh |
|
if (!m_IsIBLocked) |
|
return; |
|
|
|
RECORD_COMMAND( DX8_INDEX_DATA, 3 ); |
|
RECORD_INT( m_pIndexBuffer->UID() ); |
|
RECORD_INT( m_LockIndexBufferSize ); |
|
RECORD_STRUCT( m_LockIndexBuffer, m_LockIndexBufferSize ); |
|
|
|
Assert(m_pIndexBuffer); |
|
|
|
#ifdef CHECK_INDICES |
|
m_pIndexBuffer->UpdateShadowIndices( ( unsigned short * )m_LockIndexBuffer ); |
|
#endif // CHECK_INDICES |
|
|
|
// Unlock, and indicate how many vertices we actually used |
|
m_pIndexBuffer->Unlock(nIndexCount); |
|
m_IsIBLocked = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the entire mesh |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
ShaderUtil()->SyncMatrices(); |
|
|
|
g_ShaderMutex.Lock(); |
|
VPROF( "CMeshDX8::LockMesh" ); |
|
Lock( nVertexCount, false, *static_cast<VertexDesc_t*>( &desc ) ); |
|
if ( m_Type != MATERIAL_POINTS ) |
|
{ |
|
Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) ); |
|
} |
|
else |
|
{ |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
} |
|
|
|
CBaseMeshDX8::m_bMeshLocked = true; |
|
} |
|
|
|
|
|
void CMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
VPROF( "CMeshDX8::UnlockMesh" ); |
|
|
|
Assert( CBaseMeshDX8::m_bMeshLocked ); |
|
|
|
Unlock( nVertexCount, *static_cast<VertexDesc_t*>( &desc ) ); |
|
if ( m_Type != MATERIAL_POINTS ) |
|
{ |
|
Unlock( nIndexCount, *static_cast<IndexDesc_t*>( &desc ) ); |
|
} |
|
|
|
// The actual # we wrote |
|
m_NumVertices = nVertexCount; |
|
m_NumIndices = nIndexCount; |
|
|
|
CBaseMeshDX8::m_bMeshLocked = false; |
|
g_ShaderMutex.Unlock(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks mesh for modifying |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
VPROF( "CMeshDX8::ModifyBegin" ); |
|
|
|
// Just give the app crap buffers to fill up while we're suppressed... |
|
if ( g_pShaderDeviceDx8->IsDeactivated()) |
|
{ |
|
// Set up a bogus descriptor |
|
g_MeshMgr.ComputeVertexDescription( 0, 0, desc ); |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
return; |
|
} |
|
|
|
Assert( m_pVertexBuffer ); |
|
|
|
// Lock it baby |
|
unsigned char* pVertexMemory = m_pVertexBuffer->Modify( bReadOnly, nFirstVertex, nVertexCount ); |
|
if ( pVertexMemory ) |
|
{ |
|
m_IsVBLocked = true; |
|
g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); |
|
|
|
#ifdef RECORDING |
|
m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize; |
|
m_LockVertexBuffer = pVertexMemory; |
|
#endif |
|
} |
|
|
|
desc.m_nFirstVertex = nFirstVertex; |
|
|
|
Lock( bReadOnly, nFirstIndex, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) ); |
|
} |
|
|
|
void CMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc ); |
|
} |
|
|
|
void CMeshDX8::ModifyEnd( MeshDesc_t& desc ) |
|
{ |
|
VPROF( "CMeshDX8::ModifyEnd" ); |
|
Unlock( 0, *static_cast<IndexDesc_t*>( &desc ) ); |
|
Unlock( 0, *static_cast<VertexDesc_t*>( &desc ) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// returns the # of vertices (static meshes only) |
|
//----------------------------------------------------------------------------- |
|
int CMeshDX8::VertexCount() const |
|
{ |
|
return m_pVertexBuffer ? m_pVertexBuffer->VertexCount() : 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// returns the # of indices |
|
//----------------------------------------------------------------------------- |
|
int CMeshDX8::IndexCount( ) const |
|
{ |
|
return m_pIndexBuffer ? m_pIndexBuffer->IndexCount() : 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets up the vertex and index buffers |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::UseIndexBuffer( CIndexBuffer* pBuffer ) |
|
{ |
|
SafeAssign( &m_pIndexBuffer, pBuffer ); |
|
} |
|
|
|
void CMeshDX8::UseVertexBuffer( CVertexBuffer* pBuffer ) |
|
{ |
|
m_pVertexBuffer = pBuffer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the primitive type |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type ) |
|
{ |
|
Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) ); |
|
if ( !ShaderUtil()->OnSetPrimitiveType( this, type ) ) |
|
{ |
|
return; |
|
} |
|
|
|
LOCK_SHADERAPI(); |
|
m_Type = type; |
|
m_Mode = ComputeMode( type ); |
|
} |
|
|
|
MaterialPrimitiveType_t CMeshDX8::GetPrimitiveType( ) const |
|
{ |
|
return m_Type; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes the number of primitives we're gonna draw |
|
//----------------------------------------------------------------------------- |
|
int CMeshDX8::NumPrimitives( int nVertexCount, int nIndexCount ) const |
|
{ |
|
switch(m_Mode) |
|
{ |
|
case D3DPT_POINTLIST: |
|
return nVertexCount; |
|
|
|
case D3DPT_LINELIST: |
|
return nIndexCount / 2; |
|
|
|
case D3DPT_TRIANGLELIST: |
|
return nIndexCount / 3; |
|
|
|
case D3DPT_TRIANGLESTRIP: |
|
return nIndexCount - 2; |
|
|
|
default: |
|
// invalid, baby! |
|
Assert(0); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Checks if it's a valid format |
|
//----------------------------------------------------------------------------- |
|
#ifdef _DEBUG |
|
static void OutputVertexFormat( VertexFormat_t format ) |
|
{ |
|
// FIXME: this is a duplicate of the function in meshdx8.cpp |
|
VertexCompressionType_t compressionType = CompressionType( format ); |
|
|
|
if ( format & VERTEX_POSITION ) |
|
{ |
|
Warning( "VERTEX_POSITION|" ); |
|
} |
|
if ( format & VERTEX_NORMAL ) |
|
{ |
|
if ( compressionType == VERTEX_COMPRESSION_ON ) |
|
Warning( "VERTEX_NORMAL[COMPRESSED]|" ); |
|
else |
|
Warning( "VERTEX_NORMAL|" ); |
|
} |
|
if ( format & VERTEX_COLOR ) |
|
{ |
|
Warning( "VERTEX_COLOR|" ); |
|
} |
|
if ( format & VERTEX_SPECULAR ) |
|
{ |
|
Warning( "VERTEX_SPECULAR|" ); |
|
} |
|
if ( format & VERTEX_TANGENT_S ) |
|
{ |
|
Warning( "VERTEX_TANGENT_S|" ); |
|
} |
|
if ( format & VERTEX_TANGENT_T ) |
|
{ |
|
Warning( "VERTEX_TANGENT_T|" ); |
|
} |
|
if ( format & VERTEX_BONE_INDEX ) |
|
{ |
|
Warning( "VERTEX_BONE_INDEX|" ); |
|
} |
|
if ( format & VERTEX_FORMAT_VERTEX_SHADER ) |
|
{ |
|
Warning( "VERTEX_FORMAT_VERTEX_SHADER|" ); |
|
} |
|
Warning( "\nBone weights: %d (%s)\n", NumBoneWeights( format ), |
|
( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) ); |
|
Warning( "user data size: %d (%s)\n", UserDataSize( format ), |
|
( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) ); |
|
Warning( "num tex coords: %d\n", NumTextureCoordinates( format ) ); |
|
// NOTE: This doesn't print texcoord sizes. |
|
} |
|
#endif |
|
|
|
bool CMeshDX8::IsValidVertexFormat( VertexFormat_t vertexFormat ) |
|
{ |
|
// FIXME: Make this a debug-only check on say 6th July 2007 (after a week or so's testing) |
|
// (i.e. avoid the 360 release build perf. hit for when we ship) |
|
bool bCheckCompression = ( m_VertexFormat & VERTEX_FORMAT_COMPRESSED ) && |
|
( ( vertexFormat == VERTEX_FORMAT_INVALID ) || ( ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) ); |
|
|
|
if ( bCheckCompression || IsPC() || IsDebug() ) |
|
{ |
|
IMaterialInternal* pMaterial = ShaderAPI()->GetBoundMaterial(); |
|
Assert( pMaterial ); |
|
|
|
// the material format should match the vertex usage, unless another format is passed in |
|
if ( vertexFormat == VERTEX_FORMAT_INVALID ) |
|
{ |
|
vertexFormat = pMaterial->GetVertexUsage() & ~( VERTEX_FORMAT_VERTEX_SHADER | VERTEX_FORMAT_USE_EXACT_FORMAT ); |
|
|
|
// Blat out unused fields |
|
vertexFormat &= ~g_MeshMgr.UnusedVertexFields(); |
|
int nUnusedTextureCoords = g_MeshMgr.UnusedTextureCoords(); |
|
for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) |
|
{ |
|
if ( nUnusedTextureCoords & ( 1 << i ) ) |
|
{ |
|
vertexFormat &= ~VERTEX_TEXCOORD_MASK( i ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
vertexFormat &= ~( VERTEX_FORMAT_VERTEX_SHADER | VERTEX_FORMAT_USE_EXACT_FORMAT ); |
|
} |
|
|
|
bool bIsValid = (( VERTEX_FORMAT_FIELD_MASK & vertexFormat ) & ( VERTEX_FORMAT_FIELD_MASK & ~m_VertexFormat )) == 0; |
|
|
|
if ( m_VertexFormat & VERTEX_FORMAT_COMPRESSED ) |
|
{ |
|
// We shouldn't get compressed verts if this material doesn't support them! |
|
if ( ( vertexFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) |
|
{ |
|
static int numWarnings = 0; |
|
if ( numWarnings++ == 0 ) |
|
{ |
|
// NOTE: ComputeVertexFormat() will make sure no materials support VERTEX_FORMAT_COMPRESSED |
|
// if vertex compression is disabled in the config |
|
if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE ) |
|
Warning( "ERROR: Compressed vertices in use but vertex compression is disabled (or not supported on this hardware)!\n" ); |
|
else |
|
Warning( "ERROR: Compressed vertices in use but material does not support them!\n" ); |
|
} |
|
Assert( 0 ); |
|
bIsValid = false; |
|
} |
|
} |
|
|
|
bIsValid = bIsValid && UserDataSize( m_VertexFormat ) >= UserDataSize( vertexFormat ); |
|
|
|
for ( int i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ ) |
|
{ |
|
if ( TexCoordSize( i, m_VertexFormat ) < TexCoordSize( i, vertexFormat ) ) |
|
{ |
|
bIsValid = false; |
|
} |
|
} |
|
|
|
// NOTE: It can totally be valid to have more weights than the current number of bones. |
|
// The -1 here is because if we have N bones, we can have only (N-1) weights, |
|
// since the Nth is implied (the weights sum to 1). |
|
int nWeightCount = NumBoneWeights( m_VertexFormat ); |
|
bIsValid = bIsValid && ( nWeightCount >= ( g_pShaderAPI->GetCurrentNumBones() - 1 ) ); |
|
|
|
#ifdef _DEBUG |
|
if ( !bIsValid ) |
|
{ |
|
Warning( "Material Format:" ); |
|
if ( g_pShaderAPI->GetCurrentNumBones() > 0 ) |
|
{ |
|
vertexFormat |= VERTEX_BONE_INDEX; |
|
vertexFormat &= ~VERTEX_BONE_WEIGHT_MASK; |
|
vertexFormat |= VERTEX_BONEWEIGHT( 2 ); |
|
} |
|
|
|
OutputVertexFormat( vertexFormat ); |
|
Warning( "Mesh Format:" ); |
|
OutputVertexFormat( m_VertexFormat ); |
|
} |
|
#endif |
|
return bIsValid; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Stream source setting methods |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::SetVertexIDStreamState() |
|
{ |
|
// FIXME: this method duplicates the code in CMeshMgr::SetVertexIDStreamState |
|
|
|
if ( IsX360() ) |
|
return; |
|
|
|
bool bUsingVertexID = IsUsingVertexID(); |
|
if ( bUsingVertexID != g_bUsingVertexID ) |
|
{ |
|
if ( bUsingVertexID ) |
|
{ |
|
// NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH |
|
// It's because the indices (which are not 0 based for dynamic buffers) |
|
// are accessing both the vertexID buffer + the regular vertex buffer. |
|
// This *might* be fixable with baseVertexIndex? |
|
|
|
// NOTE: At the moment, vertex id is only used for hw morphing. I've got it |
|
// set up so that a shader that supports hw morphing always says it uses vertex id. |
|
// If we ever use vertex id for something other than hw morphing, we're going |
|
// to have to revisit how those shaders say they want to use vertex id |
|
// or fix this some other way |
|
Assert( !g_pShaderAPI->IsHWMorphingEnabled() || !m_pVertexBuffer->IsDynamic() ); |
|
|
|
CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( ); |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( pVertexIDBuffer->UID() ); |
|
RECORD_INT( 3 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( pVertexIDBuffer->VertexSize() ); |
|
|
|
D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() ); |
|
pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
} |
|
else |
|
{ |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); // vertex buffer id |
|
RECORD_INT( 3 ); // stream |
|
RECORD_INT( 0 ); // vertex offset |
|
RECORD_INT( 0 ); // vertex size |
|
|
|
D3DSetStreamSource( 3, 0, 0, 0 ); |
|
} |
|
g_bUsingVertexID = bUsingVertexID; |
|
} |
|
} |
|
|
|
void CMeshDX8::SetColorStreamState() |
|
{ |
|
if ( ( m_pColorMesh != g_pLastColorMesh ) || ( m_nColorMeshVertOffsetInBytes != g_nLastColorMeshVertOffsetInBytes ) ) |
|
{ |
|
if ( m_pColorMesh ) |
|
{ |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( m_pColorMesh->GetVertexBuffer()->UID() ); |
|
RECORD_INT( 1 ); |
|
RECORD_INT( m_nColorMeshVertOffsetInBytes ); |
|
RECORD_INT( m_pColorMesh->GetVertexBuffer()->VertexSize() ); |
|
|
|
D3DSetStreamSource( 1, m_pColorMesh->GetVertexBuffer()->GetInterface(), |
|
m_nColorMeshVertOffsetInBytes, m_pColorMesh->GetVertexBuffer()->VertexSize() ); |
|
m_pColorMesh->GetVertexBuffer()->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
} |
|
else |
|
{ |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); // vertex buffer id |
|
RECORD_INT( 1 ); // stream |
|
RECORD_INT( 0 ); // vertex offset |
|
RECORD_INT( 0 ); // vertex size |
|
|
|
D3DSetStreamSource( 1, 0, 0, 0 ); |
|
} |
|
g_pLastColorMesh = m_pColorMesh; |
|
g_nLastColorMeshVertOffsetInBytes = m_nColorMeshVertOffsetInBytes; |
|
} |
|
} |
|
|
|
void CMeshDX8::SetVertexStreamState( int nVertOffsetInBytes ) |
|
{ |
|
// Calls in here assume shader support... |
|
if ( HardwareConfig()->SupportsVertexAndPixelShaders() ) |
|
{ |
|
if ( HasFlexMesh() ) |
|
{ |
|
// m_pFlexVertexBuffer is the flex buffer down inside the CMeshMgr singleton |
|
D3DSetStreamSource( 2, m_pFlexVertexBuffer->GetInterface(), m_nFlexVertOffsetInBytes, m_pFlexVertexBuffer->VertexSize() ); |
|
|
|
if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) |
|
{ |
|
float c[4] = { 1.0f, g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ? 1.0f : 0.0f, 0.0f, 0.0f }; |
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); |
|
} |
|
|
|
g_bFlexMeshStreamSet = true; |
|
} |
|
else |
|
{ |
|
Assert( nVertOffsetInBytes == 0 ); |
|
Assert( m_pVertexBuffer ); |
|
|
|
// HACK...point stream 2 at the same VB which is bound to stream 0... |
|
// NOTE: D3D debug DLLs will RIP if stream 0 has a smaller stride than the largest |
|
// offset in the stream 2 vertex decl elements (which are position(12)+wrinkle(4)+normal(12)) |
|
// If this fires, go find the material/shader which is requesting a really 'thin' |
|
// stream 0 vertex, and fatten it up slightly (e.g. add a D3DCOLOR element) |
|
int minimumStreamZeroStride = 4 * sizeof( float ); |
|
Assert( m_pVertexBuffer->VertexSize() >= minimumStreamZeroStride ); |
|
if ( m_pVertexBuffer->VertexSize() < minimumStreamZeroStride ) |
|
{ |
|
static bool bWarned = false; |
|
if( !bWarned ) |
|
{ |
|
Warning( "Shader specifying too-thin vertex format, should be at least %d bytes! (Suppressing furthur warnings)\n", minimumStreamZeroStride ); |
|
bWarned = true; |
|
} |
|
} |
|
|
|
// Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 1 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia) |
|
// togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency. |
|
D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 ); |
|
|
|
if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) |
|
{ |
|
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; |
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); |
|
} |
|
|
|
g_bFlexMeshStreamSet = false; |
|
} |
|
} |
|
|
|
// MESHFIXME: Make sure this jives between the mesh/ib/vb version. |
|
#ifdef _X360 |
|
if ( ( g_pLastVertex != m_pVertexBuffer ) || ( m_pVertexBuffer->IsDynamic() ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) ) |
|
#else |
|
if ( ( g_pLastVertex != m_pVertexBuffer ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) ) |
|
#endif |
|
{ |
|
Assert( m_pVertexBuffer ); |
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( m_pVertexBuffer->UID() ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( nVertOffsetInBytes ); |
|
RECORD_INT( m_pVertexBuffer->VertexSize() ); |
|
|
|
D3DSetStreamSource( 0, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() ); |
|
m_pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
|
|
g_pLastVertex = m_pVertexBuffer; |
|
g_nLastVertOffsetInBytes = nVertOffsetInBytes; |
|
} |
|
} |
|
|
|
void CMeshDX8::SetIndexStreamState( int firstVertexIdx ) |
|
{ |
|
#ifdef _X360 |
|
if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( m_pIndexBuffer->IsDynamic() ) || ( firstVertexIdx != g_LastVertexIdx ) ) |
|
#else |
|
if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( firstVertexIdx != g_LastVertexIdx ) ) |
|
#endif |
|
{ |
|
Assert( m_pIndexBuffer ); |
|
|
|
RECORD_COMMAND( DX8_SET_INDICES, 2 ); |
|
RECORD_INT( m_pIndexBuffer->UID() ); |
|
RECORD_INT( firstVertexIdx ); |
|
|
|
Dx9Device()->SetIndices( m_pIndexBuffer->GetInterface() ); |
|
m_pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
m_FirstIndex = firstVertexIdx; |
|
|
|
SafeAssign( &g_pLastIndex, m_pIndexBuffer ); |
|
g_pLastIndexBuffer = NULL; |
|
g_LastVertexIdx = firstVertexIdx; |
|
} |
|
} |
|
|
|
bool CMeshDX8::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat ) |
|
{ |
|
// Can't set the state if we're deactivated |
|
if ( g_pShaderDeviceDx8->IsDeactivated() ) |
|
{ |
|
ResetMeshRenderState(); |
|
return false; |
|
} |
|
|
|
g_LastVertexFormat = vertexFormat; |
|
|
|
SetVertexIDStreamState(); |
|
SetColorStreamState(); |
|
SetVertexStreamState( nVertexOffsetInBytes ); |
|
SetIndexStreamState( nFirstVertexIdx ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws the static mesh |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::Draw( int nFirstIndex, int nIndexCount ) |
|
{ |
|
Assert( m_pVertexBuffer ); |
|
if ( !m_pVertexBuffer ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) |
|
{ |
|
MarkAsDrawn(); |
|
return; |
|
} |
|
|
|
CPrimList primList; |
|
if( nFirstIndex == -1 || nIndexCount == 0 ) |
|
{ |
|
primList.m_FirstIndex = 0; |
|
primList.m_NumIndices = m_NumIndices; |
|
} |
|
else |
|
{ |
|
primList.m_FirstIndex = nFirstIndex; |
|
primList.m_NumIndices = nIndexCount; |
|
} |
|
DrawInternal( &primList, 1 ); |
|
} |
|
|
|
void CMeshDX8::Draw( CPrimList *pLists, int nLists ) |
|
{ |
|
Assert( m_pVertexBuffer ); |
|
if ( !m_pVertexBuffer ) |
|
{ |
|
return; |
|
} |
|
|
|
if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) ) |
|
{ |
|
MarkAsDrawn(); |
|
return; |
|
} |
|
|
|
DrawInternal( pLists, nLists ); |
|
} |
|
|
|
|
|
void CMeshDX8::DrawInternal( CPrimList *pLists, int nLists ) |
|
{ |
|
HandleLateCreation(); |
|
|
|
// Make sure there's something to draw.. |
|
int i; |
|
for ( i=0; i < nLists; i++ ) |
|
{ |
|
if ( pLists[i].m_NumIndices > 0 ) |
|
break; |
|
} |
|
if ( i == nLists ) |
|
return; |
|
|
|
// can't do these in selection mode! |
|
Assert( !ShaderAPI()->IsInSelectionMode() ); |
|
|
|
if ( !SetRenderState( 0, 0 ) ) |
|
return; |
|
|
|
s_pPrims = pLists; |
|
s_nPrims = nLists; |
|
|
|
#ifdef _DEBUG |
|
for ( i = 0; i < nLists; ++i) |
|
{ |
|
Assert( pLists[i].m_NumIndices > 0 ); |
|
} |
|
#endif |
|
|
|
s_FirstVertex = 0; |
|
s_NumVertices = m_pVertexBuffer->VertexCount(); |
|
|
|
DrawMesh(); |
|
} |
|
|
|
|
|
#ifdef CHECK_INDICES |
|
void CMeshDX8::CheckIndices( CPrimList *pPrim, int numPrimitives ) |
|
{ |
|
// g_pLastVertex - this is the current vertex buffer |
|
// g_pLastColorMesh - this is the current color mesh, if there is one. |
|
// g_pLastIndex - this is the current index buffer. |
|
// vertoffset : m_FirstIndex |
|
if( m_Mode == D3DPT_TRIANGLELIST || m_Mode == D3DPT_TRIANGLESTRIP ) |
|
{ |
|
Assert( pPrim->m_FirstIndex >= 0 && pPrim->m_FirstIndex < g_pLastIndex->IndexCount() ); |
|
int i; |
|
for( i = 0; i < 2; i++ ) |
|
{ |
|
CVertexBuffer *pMesh; |
|
if( i == 0 ) |
|
{ |
|
pMesh = g_pLastVertex; |
|
Assert( pMesh ); |
|
} |
|
else |
|
{ |
|
if( !g_pLastColorMesh ) |
|
{ |
|
continue; |
|
} |
|
pMesh = g_pLastColorMesh->m_pVertexBuffer; |
|
if( !pMesh ) |
|
{ |
|
continue; |
|
} |
|
} |
|
Assert( s_FirstVertex >= 0 && |
|
(int)( s_FirstVertex + m_FirstIndex ) < pMesh->VertexCount() ); |
|
int nIndexCount = 0; |
|
if( m_Mode == D3DPT_TRIANGLELIST ) |
|
{ |
|
nIndexCount = numPrimitives * 3; |
|
} |
|
else if( m_Mode == D3DPT_TRIANGLESTRIP ) |
|
{ |
|
nIndexCount = numPrimitives + 2; |
|
} |
|
else |
|
{ |
|
Assert( 0 ); |
|
} |
|
int j; |
|
for( j = 0; j < nIndexCount; j++ ) |
|
{ |
|
int index = g_pLastIndex->GetShadowIndex( j + pPrim->m_FirstIndex ); |
|
if ( ( index < (int)s_FirstVertex ) || ( index >= (int)( s_FirstVertex + s_NumVertices ) ) ) |
|
Warning("%s invalid index: %d [%u..%u]\n", __FUNCTION__, index, s_FirstVertex, s_FirstVertex + s_NumVertices - 1 ); |
|
Assert( index >= (int)s_FirstVertex ); |
|
Assert( index < (int)(s_FirstVertex + s_NumVertices) ); |
|
} |
|
} |
|
} |
|
} |
|
#endif // CHECK_INDICES |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Actually does the dirty deed of rendering |
|
//----------------------------------------------------------------------------- |
|
void CMeshDX8::RenderPass() |
|
{ |
|
LOCK_SHADERAPI(); |
|
VPROF( "CMeshDX8::RenderPass" ); |
|
|
|
HandleLateCreation(); |
|
|
|
Assert( m_Type != MATERIAL_HETEROGENOUS ); |
|
|
|
// make sure the vertex format is a superset of the current material's |
|
// vertex format... |
|
if ( !IsValidVertexFormat( g_LastVertexFormat ) ) |
|
{ |
|
Warning( "Material %s does not support vertex format used by the mesh (maybe missing fields or mismatched vertex compression?), mesh will not be rendered. Grab a programmer!\n", |
|
ShaderAPI()->GetBoundMaterial()->GetName() ); |
|
return; |
|
} |
|
|
|
for ( int iPrim=0; iPrim < s_nPrims; iPrim++ ) |
|
{ |
|
CPrimList *pPrim = &s_pPrims[iPrim]; |
|
|
|
if ( pPrim->m_NumIndices == 0 ) |
|
continue; |
|
|
|
if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) ) |
|
{ |
|
tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "Dx9Device_DrawPrimitive" ); |
|
|
|
// (For point/instanced-quad lists, we don't actually fill in indices, but we treat it as |
|
// though there are indices for the list up until here). |
|
Dx9Device()->DrawPrimitive( m_Mode, s_FirstVertex, pPrim->m_NumIndices ); |
|
} |
|
else |
|
{ |
|
int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices ); |
|
|
|
#ifdef CHECK_INDICES |
|
CheckIndices( pPrim, numPrimitives ); |
|
#endif // CHECK_INDICES |
|
{ |
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" ); |
|
VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 ); |
|
VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "render/DrawIndexedPrimitive", COUNTER_GROUP_TELEMETRY, 1 ); |
|
VPROF_INCREMENT_GROUP_COUNTER( "render/numPrimitives", COUNTER_GROUP_TELEMETRY, 1 ); |
|
|
|
Dx9Device()->DrawIndexedPrimitive( |
|
m_Mode, // Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. |
|
|
|
m_FirstIndex, // Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer. |
|
|
|
s_FirstVertex, // Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. |
|
// The first Vertex in the vertexbuffer that we are currently using for the current batch. |
|
|
|
s_NumVertices, // Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. |
|
|
|
pPrim->m_FirstIndex, // Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer. |
|
|
|
numPrimitives );// Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. |
|
} |
|
} |
|
} |
|
|
|
if ( g_pLastVertex ) |
|
{ |
|
g_pLastVertex->MarkUsedInRendering(); |
|
} |
|
|
|
if( g_pLastIndex ) |
|
{ |
|
g_pLastIndex->MarkUsedInRendering(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Dynamic mesh implementation |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CDynamicMeshDX8::CDynamicMeshDX8() : CMeshDX8( "CDynamicMeshDX8" ) |
|
{ |
|
m_nBufferId = 0; |
|
ResetVertexAndIndexCounts(); |
|
} |
|
|
|
CDynamicMeshDX8::~CDynamicMeshDX8() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initializes the dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::Init( int nBufferId ) |
|
{ |
|
m_nBufferId = nBufferId; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Resets buffering state |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::ResetVertexAndIndexCounts() |
|
{ |
|
m_TotalVertices = m_TotalIndices = 0; |
|
m_FirstIndex = m_nFirstVertex = -1; |
|
m_HasDrawn = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Resets the state in case of a task switch |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::Reset() |
|
{ |
|
m_VertexFormat = 0; |
|
m_pVertexBuffer = 0; |
|
SafeRelease( &m_pIndexBuffer ); |
|
ResetVertexAndIndexCounts(); |
|
|
|
// Force the render state to be updated next time |
|
ResetMeshRenderState(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the vertex format associated with the dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::SetVertexFormat( VertexFormat_t format ) |
|
{ |
|
if ( g_pShaderDeviceDx8->IsDeactivated()) |
|
return; |
|
|
|
if ( CompressionType( format ) != VERTEX_COMPRESSION_NONE ) |
|
{ |
|
// UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: CMeshBuilder gets slower) |
|
Warning( "ERROR: dynamic meshes cannot use compressed vertices!\n" ); |
|
Assert( 0 ); |
|
format &= ~VERTEX_FORMAT_COMPRESSED; |
|
} |
|
|
|
if ((format != m_VertexFormat) || m_VertexOverride || m_IndexOverride) |
|
{ |
|
m_VertexFormat = format; |
|
UseVertexBuffer( g_MeshMgr.FindOrCreateVertexBuffer( m_nBufferId, format ) ); |
|
|
|
if ( m_nBufferId == 0 ) |
|
{ |
|
UseIndexBuffer( g_MeshMgr.GetDynamicIndexBuffer() ); |
|
} |
|
|
|
m_VertexOverride = m_IndexOverride = false; |
|
} |
|
} |
|
|
|
void CDynamicMeshDX8::OverrideVertexBuffer( CVertexBuffer* pVertexBuffer ) |
|
{ |
|
UseVertexBuffer( pVertexBuffer ); |
|
m_VertexOverride = true; |
|
} |
|
|
|
void CDynamicMeshDX8::OverrideIndexBuffer( CIndexBuffer* pIndexBuffer ) |
|
{ |
|
UseIndexBuffer( pIndexBuffer ); |
|
m_IndexOverride = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Do I need to reset the vertex format? |
|
//----------------------------------------------------------------------------- |
|
bool CDynamicMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const |
|
{ |
|
return m_VertexOverride || m_IndexOverride || (m_VertexFormat != fmt); |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the entire mesh |
|
//----------------------------------------------------------------------------- |
|
bool CDynamicMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const |
|
{ |
|
if ( g_pShaderDeviceDx8->IsDeactivated() ) |
|
return false; |
|
|
|
Assert( m_pVertexBuffer != NULL ); |
|
|
|
// We need space in both the vertex and index buffer |
|
return m_pVertexBuffer->HasEnoughRoom( nVertexCount ) && |
|
m_pIndexBuffer->HasEnoughRoom( nIndexCount ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// returns the number of indices in the mesh |
|
//----------------------------------------------------------------------------- |
|
int CDynamicMeshDX8::IndexCount( ) const |
|
{ |
|
return m_TotalIndices; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Operation to do pre-lock (only called for buffered meshes) |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::PreLock() |
|
{ |
|
if (m_HasDrawn) |
|
{ |
|
// Start again then |
|
ResetVertexAndIndexCounts(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the entire mesh |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s %d %d", __FUNCTION__, nVertexCount, nIndexCount ); |
|
|
|
ShaderUtil()->SyncMatrices(); |
|
|
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "g_ShaderMutex.Lock" ); |
|
g_ShaderMutex.Lock(); |
|
} |
|
|
|
// Yes, this may well also be called from BufferedMesh but that's ok |
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "PreLock" ); |
|
PreLock(); |
|
} |
|
|
|
if (m_VertexOverride) |
|
{ |
|
nVertexCount = 0; |
|
} |
|
|
|
if (m_IndexOverride) |
|
{ |
|
nIndexCount = 0; |
|
} |
|
|
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "Lock" ); |
|
Lock( nVertexCount, false, *static_cast<VertexDesc_t*>( &desc ) ); |
|
} |
|
|
|
if (m_nFirstVertex < 0) |
|
{ |
|
m_nFirstVertex = desc.m_nFirstVertex; |
|
} |
|
|
|
// When we're using a static index buffer or a flex mesh, the indices assume vertices start at 0 |
|
if ( m_IndexOverride || HasFlexMesh() ) |
|
{ |
|
desc.m_nFirstVertex -= m_nFirstVertex; |
|
} |
|
|
|
// Don't add indices for points; DrawIndexedPrimitive not supported for them. |
|
if ( m_Type != MATERIAL_POINTS ) |
|
{ |
|
int nFirstIndex; |
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "Lock nFirstIndex" ); |
|
nFirstIndex = Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) ); |
|
} |
|
if (m_FirstIndex < 0) |
|
{ |
|
m_FirstIndex = nFirstIndex; |
|
} |
|
} |
|
else |
|
{ |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
} |
|
|
|
CBaseMeshDX8::m_bMeshLocked = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Unlocks the mesh |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
m_TotalVertices += nVertexCount; |
|
m_TotalIndices += nIndexCount; |
|
|
|
if (DebugTrace()) |
|
{ |
|
Spew( nVertexCount, nIndexCount, desc ); |
|
} |
|
|
|
CMeshDX8::UnlockMesh( nVertexCount, nIndexCount, desc ); |
|
|
|
// This is handled in the CMeshDX8::UnlockMesh above. |
|
//CBaseMeshDX8::m_bMeshLocked = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws it |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::Draw( int nFirstIndex, int nIndexCount ) |
|
{ |
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) |
|
{ |
|
MarkAsDrawn(); |
|
return; |
|
} |
|
|
|
VPROF( "CDynamicMeshDX8::Draw" ); |
|
|
|
m_HasDrawn = true; |
|
|
|
if (m_IndexOverride || m_VertexOverride || |
|
( ( m_TotalVertices > 0 ) && ( m_TotalIndices > 0 || m_Type == MATERIAL_POINTS || m_Type == MATERIAL_INSTANCED_QUADS ) ) ) |
|
{ |
|
Assert( !m_IsDrawing ); |
|
|
|
HandleLateCreation( ); |
|
|
|
// only have a non-zero first vertex when we are using static indices |
|
int nFirstVertex = m_VertexOverride ? 0 : m_nFirstVertex; |
|
int actualFirstVertex = m_IndexOverride ? nFirstVertex : 0; |
|
int nVertexOffsetInBytes = HasFlexMesh() ? nFirstVertex * g_MeshMgr.VertexFormatSize( GetVertexFormat() ) : 0; |
|
int baseIndex = m_IndexOverride ? 0 : m_FirstIndex; |
|
|
|
// Overriding with the dynamic index buffer, preserve state! |
|
if ( m_IndexOverride && m_pIndexBuffer == g_MeshMgr.GetDynamicIndexBuffer() ) |
|
{ |
|
baseIndex = m_FirstIndex; |
|
} |
|
|
|
VertexFormat_t fmt = m_VertexOverride ? GetVertexFormat() : VERTEX_FORMAT_INVALID; |
|
if ( !SetRenderState( nVertexOffsetInBytes, actualFirstVertex, fmt ) ) |
|
return; |
|
|
|
// Draws a portion of the mesh |
|
int numVertices = m_VertexOverride ? m_pVertexBuffer->VertexCount() : m_TotalVertices; |
|
if ((nFirstIndex != -1) && (nIndexCount != 0)) |
|
{ |
|
nFirstIndex += baseIndex; |
|
} |
|
else |
|
{ |
|
// by default we draw the whole thing |
|
nFirstIndex = baseIndex; |
|
if( m_IndexOverride ) |
|
{ |
|
nIndexCount = m_pIndexBuffer->IndexCount(); |
|
Assert( nIndexCount != 0 ); |
|
} |
|
else |
|
{ |
|
nIndexCount = m_TotalIndices; |
|
// Fake out the index count if we're drawing points/instanced-quads |
|
if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) ) |
|
{ |
|
nIndexCount = m_TotalVertices; |
|
} |
|
Assert( nIndexCount != 0 ); |
|
} |
|
} |
|
|
|
// Fix up nFirstVertex to indicate the first vertex used in the data |
|
if ( !HasFlexMesh() ) |
|
{ |
|
actualFirstVertex = nFirstVertex - actualFirstVertex; |
|
} |
|
|
|
s_FirstVertex = actualFirstVertex; |
|
s_NumVertices = numVertices; |
|
|
|
// Build a primlist with 1 element.. |
|
CPrimList prim; |
|
prim.m_FirstIndex = nFirstIndex; |
|
prim.m_NumIndices = nIndexCount; |
|
Assert( nIndexCount != 0 ); |
|
s_pPrims = &prim; |
|
s_nPrims = 1; |
|
|
|
DrawMesh(); |
|
|
|
s_pPrims = NULL; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// This is useful when we need to dynamically modify data; just set the |
|
// render state and draw the pass immediately |
|
//----------------------------------------------------------------------------- |
|
void CDynamicMeshDX8::DrawSinglePassImmediately() |
|
{ |
|
if ((m_TotalVertices > 0) || (m_TotalIndices > 0)) |
|
{ |
|
Assert( !m_IsDrawing ); |
|
|
|
// Set the render state |
|
if ( SetRenderState( 0, 0 ) ) |
|
{ |
|
s_FirstVertex = m_nFirstVertex; |
|
s_NumVertices = m_TotalVertices; |
|
|
|
// Make a temporary PrimList to hold the indices. |
|
CPrimList prim( m_FirstIndex, m_TotalIndices ); |
|
Assert( m_TotalIndices != 0 ); |
|
s_pPrims = &prim; |
|
s_nPrims = 1; |
|
|
|
// Render it |
|
RenderPass(); |
|
} |
|
|
|
// We're done with our data |
|
ResetVertexAndIndexCounts(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// A mesh that stores temporary vertex data in the correct format (for modification) |
|
// |
|
//----------------------------------------------------------------------------- |
|
// Used in rendering sub-parts of the mesh |
|
unsigned int CTempMeshDX8::s_NumIndices; |
|
unsigned int CTempMeshDX8::s_FirstIndex; |
|
|
|
//----------------------------------------------------------------------------- |
|
// constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CTempMeshDX8::CTempMeshDX8( bool isDynamic ) : m_VertexSize(0xFFFF), m_IsDynamic(isDynamic) |
|
{ |
|
#ifdef DBGFLAG_ASSERT |
|
m_Locked = false; |
|
m_InPass = false; |
|
#endif |
|
} |
|
|
|
CTempMeshDX8::~CTempMeshDX8() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Is the temp mesh dynamic? |
|
//----------------------------------------------------------------------------- |
|
bool CTempMeshDX8::IsDynamic() const |
|
{ |
|
return m_IsDynamic; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the vertex format |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::SetVertexFormat( VertexFormat_t format ) |
|
{ |
|
CBaseMeshDX8::SetVertexFormat(format); |
|
m_VertexSize = g_MeshMgr.VertexFormatSize( format ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// returns the # of vertices (static meshes only) |
|
//----------------------------------------------------------------------------- |
|
int CTempMeshDX8::VertexCount() const |
|
{ |
|
return m_VertexSize ? m_VertexData.Count() / m_VertexSize : 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// returns the # of indices |
|
//----------------------------------------------------------------------------- |
|
int CTempMeshDX8::IndexCount( ) const |
|
{ |
|
return m_IndexData.Count(); |
|
} |
|
|
|
void CTempMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
Assert( !m_Locked ); |
|
|
|
m_LockedVerts = nVertexCount; |
|
m_LockedIndices = nIndexCount; |
|
|
|
if( nVertexCount > 0 ) |
|
{ |
|
int vertexByteOffset = m_VertexSize * nFirstVertex; |
|
|
|
// Lock it baby |
|
unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset]; |
|
|
|
// Compute the vertex index.. |
|
desc.m_nFirstVertex = vertexByteOffset / m_VertexSize; |
|
|
|
// Set up the mesh descriptor |
|
g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); |
|
} |
|
else |
|
{ |
|
desc.m_nFirstVertex = 0; |
|
// Set up the mesh descriptor |
|
g_MeshMgr.ComputeVertexDescription( 0, 0, desc ); |
|
} |
|
|
|
if (m_Type != MATERIAL_POINTS && nIndexCount > 0 ) |
|
{ |
|
desc.m_pIndices = &m_IndexData[nFirstIndex]; |
|
desc.m_nIndexSize = 1; |
|
} |
|
else |
|
{ |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
} |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
m_Locked = true; |
|
#endif |
|
} |
|
|
|
void CTempMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc ); |
|
} |
|
|
|
void CTempMeshDX8::ModifyEnd( MeshDesc_t& desc ) |
|
{ |
|
#ifdef DBGFLAG_ASSERT |
|
Assert( m_Locked ); |
|
m_Locked = false; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the mesh |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
ShaderUtil()->SyncMatrices(); |
|
|
|
g_ShaderMutex.Lock(); |
|
|
|
Assert( !m_Locked ); |
|
|
|
m_LockedVerts = nVertexCount; |
|
m_LockedIndices = nIndexCount; |
|
|
|
if( nVertexCount > 0 ) |
|
{ |
|
int vertexByteOffset = m_VertexData.AddMultipleToTail( m_VertexSize * nVertexCount ); |
|
|
|
// Lock it baby |
|
unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset]; |
|
|
|
// Compute the vertex index.. |
|
desc.m_nFirstVertex = vertexByteOffset / m_VertexSize; |
|
|
|
// Set up the mesh descriptor |
|
g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc ); |
|
} |
|
else |
|
{ |
|
desc.m_nFirstVertex = 0; |
|
// Set up the mesh descriptor |
|
g_MeshMgr.ComputeVertexDescription( 0, 0, desc ); |
|
} |
|
|
|
if (m_Type != MATERIAL_POINTS && nIndexCount > 0 ) |
|
{ |
|
int nFirstIndex = m_IndexData.AddMultipleToTail( nIndexCount ); |
|
desc.m_pIndices = &m_IndexData[nFirstIndex]; |
|
desc.m_nIndexSize = 1; |
|
} |
|
else |
|
{ |
|
desc.m_pIndices = g_nScratchIndexBuffer; |
|
desc.m_nIndexSize = 0; |
|
} |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
m_Locked = true; |
|
#endif |
|
|
|
CBaseMeshDX8::m_bMeshLocked = true; |
|
} |
|
|
|
void CTempMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
Assert( m_Locked ); |
|
|
|
// Remove unused vertices and indices |
|
int verticesToRemove = m_LockedVerts - nVertexCount; |
|
if( verticesToRemove != 0 ) |
|
{ |
|
m_VertexData.RemoveMultiple( m_VertexData.Count() - verticesToRemove, verticesToRemove ); |
|
} |
|
|
|
int indicesToRemove = m_LockedIndices - nIndexCount; |
|
if( indicesToRemove != 0 ) |
|
{ |
|
m_IndexData.RemoveMultiple( m_IndexData.Count() - indicesToRemove, indicesToRemove ); |
|
} |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
m_Locked = false; |
|
#endif |
|
|
|
CBaseMeshDX8::m_bMeshLocked = false; |
|
|
|
g_ShaderMutex.Unlock(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the primitive type |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type ) |
|
{ |
|
// FIXME: Support MATERIAL_INSTANCED_QUADS for CTempMeshDX8 (X360 only) |
|
Assert( ( type != MATERIAL_INSTANCED_QUADS ) /* || IsX360() */ ); |
|
m_Type = type; |
|
} |
|
|
|
MaterialPrimitiveType_t CTempMeshDX8::GetPrimitiveType( ) const |
|
{ |
|
return m_Type; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets the dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
CDynamicMeshDX8* CTempMeshDX8::GetDynamicMesh( ) |
|
{ |
|
return static_cast<CDynamicMeshDX8*>(g_MeshMgr.GetActualDynamicMesh( m_VertexFormat )); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws the entire mesh |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::Draw( int nFirstIndex, int nIndexCount ) |
|
{ |
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) |
|
{ |
|
MarkAsDrawn(); |
|
return; |
|
} |
|
|
|
if (m_VertexData.Count() > 0) |
|
{ |
|
if ( !g_pShaderDeviceDx8->IsDeactivated() ) |
|
{ |
|
#ifdef DRAW_SELECTION |
|
if (!g_bDrawSelection && !ShaderAPI()->IsInSelectionMode()) |
|
#else |
|
if (!ShaderAPI()->IsInSelectionMode()) |
|
#endif |
|
{ |
|
s_FirstIndex = nFirstIndex; |
|
s_NumIndices = nIndexCount; |
|
|
|
DrawMesh( ); |
|
|
|
// This assertion fails if a BeginPass() call was not matched by |
|
// a RenderPass() call |
|
Assert(!m_InPass); |
|
} |
|
else |
|
{ |
|
TestSelection(); |
|
} |
|
} |
|
|
|
// Clear out the data if this temp mesh is a dynamic one... |
|
if (m_IsDynamic) |
|
{ |
|
m_VertexData.RemoveAll(); |
|
m_IndexData.RemoveAll(); |
|
} |
|
} |
|
} |
|
|
|
void CTempMeshDX8::CopyToMeshBuilder( |
|
int iStartVert, // Which vertices to copy. |
|
int nVerts, |
|
int iStartIndex, // Which indices to copy. |
|
int nIndices, |
|
int indexOffset, // This is added to each index. |
|
CMeshBuilder &builder ) |
|
{ |
|
int startOffset = iStartVert * m_VertexSize; |
|
int endOffset = (iStartVert + nVerts) * m_VertexSize; |
|
Assert( startOffset >= 0 && startOffset <= m_VertexData.Count() ); |
|
Assert( endOffset >= 0 && endOffset <= m_VertexData.Count() && endOffset >= startOffset ); |
|
if ( endOffset > startOffset ) |
|
{ |
|
// FIXME: make this a method of CMeshBuilder (so the 'Position' pointer accessor can be removed) |
|
// make sure it takes a VertexFormat_t parameter for src/dest match validation |
|
memcpy( (void*)builder.Position(), &m_VertexData[startOffset], endOffset - startOffset ); |
|
builder.AdvanceVertices( nVerts ); |
|
} |
|
|
|
for ( int i = 0; i < nIndices; ++i ) |
|
{ |
|
builder.Index( m_IndexData[iStartIndex+i] + indexOffset ); |
|
builder.AdvanceIndex(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Selection mode helper functions |
|
//----------------------------------------------------------------------------- |
|
static void ComputeModelToView( D3DXMATRIX& modelToView ) |
|
{ |
|
// Get the modelview matrix... |
|
D3DXMATRIX world, view; |
|
ShaderAPI()->GetMatrix( MATERIAL_MODEL, (float*)&world ); |
|
ShaderAPI()->GetMatrix( MATERIAL_VIEW, (float*)&view ); |
|
D3DXMatrixMultiply( &modelToView, &world, &view ); |
|
} |
|
|
|
static float ComputeCullFactor( ) |
|
{ |
|
D3DCULL cullMode = ShaderAPI()->GetCullMode(); |
|
|
|
float cullFactor; |
|
switch(cullMode) |
|
{ |
|
case D3DCULL_CCW: |
|
cullFactor = -1.0f; |
|
break; |
|
|
|
case D3DCULL_CW: |
|
cullFactor = 1.0f; |
|
break; |
|
|
|
default: |
|
cullFactor = 0.0f; |
|
break; |
|
}; |
|
|
|
return cullFactor; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Clip to viewport |
|
//----------------------------------------------------------------------------- |
|
static int g_NumClipVerts; |
|
static D3DXVECTOR3 g_ClipVerts[16]; |
|
|
|
static bool PointInsidePlane( D3DXVECTOR3* pVert, int normalInd, float val, bool nearClip ) |
|
{ |
|
if ((val > 0) || nearClip) |
|
return (val - (*pVert)[normalInd] >= 0); |
|
else |
|
return ((*pVert)[normalInd] - val >= 0); |
|
} |
|
|
|
static void IntersectPlane( D3DXVECTOR3* pStart, D3DXVECTOR3* pEnd, |
|
int normalInd, float val, D3DXVECTOR3* pOutVert ) |
|
{ |
|
D3DXVECTOR3 dir; |
|
D3DXVec3Subtract( &dir, pEnd, pStart ); |
|
Assert( dir[normalInd] != 0.0f ); |
|
float t = (val - (*pStart)[normalInd]) / dir[normalInd]; |
|
pOutVert->x = pStart->x + dir.x * t; |
|
pOutVert->y = pStart->y + dir.y * t; |
|
pOutVert->z = pStart->z + dir.z * t; |
|
|
|
// Avoid any precision problems. |
|
(*pOutVert)[normalInd] = val; |
|
} |
|
|
|
static int ClipTriangleAgainstPlane( D3DXVECTOR3** ppVert, int nVertexCount, |
|
D3DXVECTOR3** ppOutVert, int normalInd, float val, bool nearClip = false ) |
|
{ |
|
// Ye Olde Sutherland-Hodgman clipping algorithm |
|
int numOutVerts = 0; |
|
D3DXVECTOR3* pStart = ppVert[nVertexCount-1]; |
|
bool startInside = PointInsidePlane( pStart, normalInd, val, nearClip ); |
|
for (int i = 0; i < nVertexCount; ++i) |
|
{ |
|
D3DXVECTOR3* pEnd = ppVert[i]; |
|
bool endInside = PointInsidePlane( pEnd, normalInd, val, nearClip ); |
|
if (endInside) |
|
{ |
|
if (!startInside) |
|
{ |
|
IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] ); |
|
ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++]; |
|
} |
|
ppOutVert[numOutVerts++] = pEnd; |
|
} |
|
else |
|
{ |
|
if (startInside) |
|
{ |
|
IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] ); |
|
ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++]; |
|
} |
|
} |
|
pStart = pEnd; |
|
startInside = endInside; |
|
} |
|
|
|
return numOutVerts; |
|
} |
|
|
|
void CTempMeshDX8::ClipTriangle( D3DXVECTOR3** ppVert, float zNear, D3DXMATRIX& projection ) |
|
{ |
|
int i; |
|
int nVertexCount = 3; |
|
D3DXVECTOR3* ppClipVert1[10]; |
|
D3DXVECTOR3* ppClipVert2[10]; |
|
|
|
g_NumClipVerts = 0; |
|
|
|
// Clip against the near plane in view space to prevent negative w. |
|
// Clip against each plane |
|
nVertexCount = ClipTriangleAgainstPlane( ppVert, nVertexCount, ppClipVert1, 2, zNear, true ); |
|
if (nVertexCount < 3) |
|
return; |
|
|
|
// Sucks that I have to do this, but I have to clip near plane in view space |
|
// Clipping in projection space is screwy when w < 0 |
|
// Transform the clipped points into projection space |
|
Assert( g_NumClipVerts <= 2 ); |
|
for (i = 0; i < nVertexCount; ++i) |
|
{ |
|
if (ppClipVert1[i] == &g_ClipVerts[0]) |
|
{ |
|
D3DXVec3TransformCoord( &g_ClipVerts[0], ppClipVert1[i], &projection ); |
|
} |
|
else if (ppClipVert1[i] == &g_ClipVerts[1]) |
|
{ |
|
D3DXVec3TransformCoord( &g_ClipVerts[1], ppClipVert1[i], &projection ); |
|
} |
|
else |
|
{ |
|
D3DXVec3TransformCoord( &g_ClipVerts[g_NumClipVerts], ppClipVert1[i], &projection ); |
|
ppClipVert1[i] = &g_ClipVerts[g_NumClipVerts]; |
|
++g_NumClipVerts; |
|
} |
|
} |
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 2, 1.0f ); |
|
if (nVertexCount < 3) |
|
return; |
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 0, 1.0f ); |
|
if (nVertexCount < 3) |
|
return; |
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 0, -1.0f ); |
|
if (nVertexCount < 3) |
|
return; |
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 1, 1.0f ); |
|
if (nVertexCount < 3) |
|
return; |
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 1, -1.0f ); |
|
if (nVertexCount < 3) |
|
return; |
|
|
|
#ifdef DRAW_SELECTION |
|
if( 1 || g_bDrawSelection ) |
|
{ |
|
srand( *(int*)(&ppClipVert2[0]->x) ); |
|
unsigned char r = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64; |
|
unsigned char g = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64; |
|
unsigned char b = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64; |
|
|
|
ShaderAPI()->SetupSelectionModeVisualizationState(); |
|
|
|
CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder(); |
|
IMesh* pMesh = GetDynamicMesh(); |
|
pMeshBuilder->Begin( pMesh, MATERIAL_POLYGON, nVertexCount ); |
|
|
|
for ( i = 0; i < nVertexCount; ++i) |
|
{ |
|
pMeshBuilder->Position3fv( *ppClipVert2[i] ); |
|
pMeshBuilder->Color3ub( r, g, b ); |
|
pMeshBuilder->AdvanceVertex(); |
|
} |
|
|
|
pMeshBuilder->End(); |
|
pMesh->Draw(); |
|
|
|
pMeshBuilder->Begin( pMesh, MATERIAL_LINE_LOOP, nVertexCount ); |
|
|
|
for ( i = 0; i < nVertexCount; ++i) |
|
{ |
|
pMeshBuilder->Position3fv( *ppClipVert2[i] ); |
|
pMeshBuilder->Color3ub( 255, 255, 255 ); |
|
pMeshBuilder->AdvanceVertex(); |
|
} |
|
|
|
pMeshBuilder->End(); |
|
pMesh->Draw(); |
|
} |
|
#endif |
|
|
|
// Compute closest and furthest verts |
|
float minz = ppClipVert2[0]->z; |
|
float maxz = ppClipVert2[0]->z; |
|
for ( i = 1; i < nVertexCount; ++i ) |
|
{ |
|
if (ppClipVert2[i]->z < minz) |
|
minz = ppClipVert2[i]->z; |
|
else if (ppClipVert2[i]->z > maxz) |
|
maxz = ppClipVert2[i]->z; |
|
} |
|
|
|
ShaderAPI()->RegisterSelectionHit( minz, maxz ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Selection mode |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::TestSelection() |
|
{ |
|
// Note that this doesn't take into account any vertex modification |
|
// done in a vertex shader. Also it doesn't take into account any clipping |
|
// done in hardware |
|
|
|
// Blow off points and lines; they don't matter |
|
if ((m_Type != MATERIAL_TRIANGLES) && (m_Type != MATERIAL_TRIANGLE_STRIP)) |
|
return; |
|
|
|
D3DXMATRIX modelToView, projection; |
|
ComputeModelToView( modelToView ); |
|
ShaderAPI()->GetMatrix( MATERIAL_PROJECTION, (float*)&projection ); |
|
float zNear = -projection.m[3][2] / projection.m[2][2]; |
|
|
|
D3DXVECTOR3* pPos[3]; |
|
D3DXVECTOR3 edge[2]; |
|
D3DXVECTOR3 normal; |
|
|
|
int numTriangles; |
|
if (m_Type == MATERIAL_TRIANGLES) |
|
numTriangles = m_IndexData.Count() / 3; |
|
else |
|
numTriangles = m_IndexData.Count() - 2; |
|
|
|
float cullFactor = ComputeCullFactor(); |
|
|
|
// Makes the lovely loop simpler |
|
if (m_Type == MATERIAL_TRIANGLE_STRIP) |
|
cullFactor *= -1.0f; |
|
|
|
// We'll need some temporary memory to tell us if we're transformed the vert |
|
int nVertexCount = m_VertexData.Count() / m_VertexSize; |
|
static CUtlVector< unsigned char > transformedVert; |
|
int transformedVertSize = (nVertexCount + 7) >> 3; |
|
transformedVert.RemoveAll(); |
|
transformedVert.EnsureCapacity( transformedVertSize ); |
|
transformedVert.AddMultipleToTail( transformedVertSize ); |
|
memset( transformedVert.Base(), 0, transformedVertSize ); |
|
|
|
int indexPos; |
|
for (int i = 0; i < numTriangles; ++i) |
|
{ |
|
// Get the three indices |
|
if (m_Type == MATERIAL_TRIANGLES) |
|
{ |
|
indexPos = i * 3; |
|
} |
|
else |
|
{ |
|
Assert( m_Type == MATERIAL_TRIANGLE_STRIP ); |
|
cullFactor *= -1.0f; |
|
indexPos = i; |
|
} |
|
|
|
// BAH. Gotta clip to the near clip plane in view space to prevent |
|
// negative w coords; negative coords throw off the projection-space clipper. |
|
|
|
// Get the three positions in view space |
|
int inFrontIdx = -1; |
|
for (int j = 0; j < 3; ++j) |
|
{ |
|
int index = m_IndexData[indexPos]; |
|
D3DXVECTOR3* pPosition = (D3DXVECTOR3*)&m_VertexData[index * m_VertexSize]; |
|
if ((transformedVert[index >> 3] & (1 << (index & 0x7))) == 0) |
|
{ |
|
D3DXVec3TransformCoord( pPosition, pPosition, &modelToView ); |
|
transformedVert[index >> 3] |= (1 << (index & 0x7)); |
|
} |
|
|
|
pPos[j] = pPosition; |
|
if (pPos[j]->z < 0.0f) |
|
inFrontIdx = j; |
|
++indexPos; |
|
} |
|
|
|
// all points are behind the camera |
|
if (inFrontIdx < 0) |
|
continue; |
|
|
|
// backface cull.... |
|
D3DXVec3Subtract( &edge[0], pPos[1], pPos[0] ); |
|
D3DXVec3Subtract( &edge[1], pPos[2], pPos[0] ); |
|
D3DXVec3Cross( &normal, &edge[0], &edge[1] ); |
|
float dot = D3DXVec3Dot( &normal, pPos[inFrontIdx] ); |
|
if (dot * cullFactor > 0.0f) |
|
continue; |
|
|
|
// Clip to viewport |
|
ClipTriangle( pPos, zNear, projection ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Begins a render pass |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::BeginPass( ) |
|
{ |
|
Assert( !m_InPass ); |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
m_InPass = true; |
|
#endif |
|
|
|
CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder(); |
|
|
|
CDynamicMeshDX8* pMesh = GetDynamicMesh( ); |
|
|
|
int nIndexCount; |
|
int nFirstIndex; |
|
if ((s_FirstIndex == -1) && (s_NumIndices == 0)) |
|
{ |
|
nIndexCount = m_IndexData.Count(); |
|
nFirstIndex = 0; |
|
} |
|
else |
|
{ |
|
nIndexCount = s_NumIndices; |
|
nFirstIndex = s_FirstIndex; |
|
} |
|
|
|
int i; |
|
int nVertexCount = m_VertexData.Count() / m_VertexSize; |
|
pMeshBuilder->Begin( pMesh, m_Type, nVertexCount, nIndexCount ); |
|
|
|
// Copy in the vertex data... |
|
// Note that since we pad the vertices, it's faster for us to simply |
|
// copy the fields we're using... |
|
Assert( pMeshBuilder->BaseVertexData() ); |
|
memcpy( pMeshBuilder->BaseVertexData(), m_VertexData.Base(), m_VertexData.Count() ); |
|
pMeshBuilder->AdvanceVertices( m_VertexData.Count() / m_VertexSize ); |
|
|
|
for ( i = 0; i < nIndexCount; ++i ) |
|
{ |
|
pMeshBuilder->Index( m_IndexData[nFirstIndex+i] ); |
|
pMeshBuilder->AdvanceIndex(); |
|
} |
|
|
|
// NOTE: The client is expected to modify the data after this call is made |
|
pMeshBuilder->Reset(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Draws a single pass |
|
//----------------------------------------------------------------------------- |
|
void CTempMeshDX8::RenderPass() |
|
{ |
|
Assert( m_InPass ); |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
m_InPass = false; |
|
#endif |
|
|
|
// Have the shader API modify the vertex data as it needs |
|
// This vertex data is modified based on state set by the material |
|
ShaderAPI()->ModifyVertexData( ); |
|
|
|
// Done building the mesh |
|
ShaderAPI()->GetVertexModifyBuilder()->End(); |
|
|
|
// Have the dynamic mesh render a single pass... |
|
GetDynamicMesh()->DrawSinglePassImmediately(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Buffered mesh implementation |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
|
|
CBufferedMeshDX8::CBufferedMeshDX8() : m_IsFlushing(false), m_WasRendered(true) |
|
{ |
|
m_pMesh = NULL; |
|
#ifdef DEBUG_BUFFERED_STATE |
|
m_BufferedStateSet = false; |
|
#endif |
|
} |
|
|
|
CBufferedMeshDX8::~CBufferedMeshDX8() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the mesh |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::SetMesh( CBaseMeshDX8* pMesh ) |
|
{ |
|
if (m_pMesh != pMesh) |
|
{ |
|
ShaderAPI()->FlushBufferedPrimitives(); |
|
m_pMesh = pMesh; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Spews the mesh data |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc ) |
|
{ |
|
if ( m_pMesh ) |
|
{ |
|
m_pMesh->Spew( nVertexCount, nIndexCount, spewDesc ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the material |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::SetVertexFormat( VertexFormat_t format ) |
|
{ |
|
Assert( m_pMesh ); |
|
if (m_pMesh->NeedsVertexFormatReset(format)) |
|
{ |
|
ShaderAPI()->FlushBufferedPrimitives(); |
|
m_pMesh->SetVertexFormat( format ); |
|
} |
|
} |
|
|
|
void CBufferedMeshDX8::SetMorphFormat( MorphFormat_t format ) |
|
{ |
|
Assert( m_pMesh ); |
|
m_pMesh->SetMorphFormat( format ); |
|
} |
|
|
|
VertexFormat_t CBufferedMeshDX8::GetVertexFormat( ) const |
|
{ |
|
Assert( m_pMesh ); |
|
return m_pMesh->GetVertexFormat(); |
|
} |
|
|
|
void CBufferedMeshDX8::SetMaterial( IMaterial* pMaterial ) |
|
{ |
|
#if _DEBUG |
|
Assert( m_pMesh ); |
|
m_pMesh->SetMaterial( pMaterial ); |
|
#endif |
|
} |
|
|
|
void CBufferedMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & spewDesc ) |
|
{ |
|
#if _DEBUG |
|
Assert( m_pMesh ); |
|
m_pMesh->ValidateData( nVertexCount, nIndexCount, spewDesc ); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the flex mesh to render with this mesh |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes ) |
|
{ |
|
// FIXME: Probably are situations where we don't need to flush, |
|
// but this is going to look different in a very short while, so I'm not going to bother |
|
ShaderAPI()->FlushBufferedPrimitives(); |
|
m_pMesh->SetFlexMesh( pMesh, nVertexOffsetInBytes ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// checks to see if it was rendered.. |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::ResetRendered() |
|
{ |
|
m_WasRendered = false; |
|
} |
|
|
|
bool CBufferedMeshDX8::WasNotRendered() const |
|
{ |
|
return !m_WasRendered; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// "Draws" it |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::Draw( int nFirstIndex, int nIndexCount ) |
|
{ |
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) ) |
|
{ |
|
m_WasRendered = true; |
|
MarkAsDrawn(); |
|
return; |
|
} |
|
|
|
Assert( !m_IsFlushing && !m_WasRendered ); |
|
|
|
// Gotta draw all of the buffered mesh |
|
Assert( (nFirstIndex == -1) && (nIndexCount == 0) ); |
|
|
|
// No need to draw it more than once... |
|
m_WasRendered = true; |
|
|
|
// We've got something to flush |
|
m_FlushNeeded = true; |
|
|
|
// Less than 0 indices indicates we were using a standard buffer |
|
if ( m_pMesh->HasFlexMesh() || !ShaderUtil()->GetConfig().bBufferPrimitives ) |
|
{ |
|
ShaderAPI()->FlushBufferedPrimitives(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the primitive mode |
|
//----------------------------------------------------------------------------- |
|
|
|
void CBufferedMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type ) |
|
{ |
|
Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) ); |
|
Assert( type != MATERIAL_HETEROGENOUS ); |
|
|
|
if (type != GetPrimitiveType()) |
|
{ |
|
ShaderAPI()->FlushBufferedPrimitives(); |
|
m_pMesh->SetPrimitiveType(type); |
|
} |
|
} |
|
|
|
MaterialPrimitiveType_t CBufferedMeshDX8::GetPrimitiveType( ) const |
|
{ |
|
return m_pMesh->GetPrimitiveType(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the entire mesh |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
ShaderUtil()->SyncMatrices(); |
|
|
|
Assert( m_pMesh ); |
|
Assert( m_WasRendered ); |
|
|
|
// Do some pre-lock processing |
|
m_pMesh->PreLock(); |
|
|
|
// for tristrips, gotta make degenerate ones... |
|
m_ExtraIndices = 0; |
|
bool tristripFixup = (m_pMesh->IndexCount() != 0) && |
|
(m_pMesh->GetPrimitiveType() == MATERIAL_TRIANGLE_STRIP); |
|
if (tristripFixup) |
|
{ |
|
m_ExtraIndices = (m_pMesh->IndexCount() & 0x1) != 0 ? 3 : 2; |
|
nIndexCount += m_ExtraIndices; |
|
} |
|
|
|
// Flush if we gotta |
|
if (!m_pMesh->HasEnoughRoom(nVertexCount, nIndexCount)) |
|
{ |
|
ShaderAPI()->FlushBufferedPrimitives(); |
|
} |
|
|
|
m_pMesh->LockMesh( nVertexCount, nIndexCount, desc ); |
|
|
|
// This is taken care of in the function above. |
|
// CBaseMeshDX8::m_bMeshLocked = true; |
|
|
|
// Deal with fixing up the tristrip.. |
|
if ( tristripFixup && desc.m_nIndexSize ) |
|
{ |
|
char buf[32]; |
|
if (DebugTrace()) |
|
{ |
|
if (m_ExtraIndices == 3) |
|
sprintf(buf,"Link Index: %d %d\n", m_LastIndex, m_LastIndex); |
|
else |
|
sprintf(buf,"Link Index: %d\n", m_LastIndex); |
|
Plat_DebugString(buf); |
|
} |
|
*desc.m_pIndices++ = m_LastIndex; |
|
if (m_ExtraIndices == 3) |
|
{ |
|
*desc.m_pIndices++ = m_LastIndex; |
|
} |
|
|
|
// Leave room for the last padding index |
|
++desc.m_pIndices; |
|
} |
|
|
|
m_WasRendered = false; |
|
|
|
#ifdef DEBUG_BUFFERED_MESHES |
|
if (m_BufferedStateSet) |
|
{ |
|
BufferedState_t compare; |
|
ShaderAPI()->GetBufferedState( compare ); |
|
Assert( !memcmp( &compare, &m_BufferedState, sizeof(compare) ) ); |
|
} |
|
else |
|
{ |
|
ShaderAPI()->GetBufferedState( m_BufferedState ); |
|
m_BufferedStateSet = true; |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locks/unlocks the entire mesh |
|
//----------------------------------------------------------------------------- |
|
void CBufferedMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc ) |
|
{ |
|
Assert( m_pMesh ); |
|
|
|
// Gotta fix up the first index to batch strips reasonably |
|
if ((m_pMesh->GetPrimitiveType() == MATERIAL_TRIANGLE_STRIP) && desc.m_nIndexSize ) |
|
{ |
|
if (m_ExtraIndices > 0) |
|
{ |
|
*(desc.m_pIndices - 1) = *desc.m_pIndices; |
|
|
|
if (DebugTrace()) |
|
{ |
|
char buf[32]; |
|
sprintf(buf,"Link Index: %d\n", *desc.m_pIndices); |
|
Plat_DebugString(buf); |
|
} |
|
} |
|
|
|
// Remember the last index for next time |
|
m_LastIndex = desc.m_pIndices[nIndexCount - 1]; |
|
|
|
nIndexCount += m_ExtraIndices; |
|
} |
|
|
|
m_pMesh->UnlockMesh( nVertexCount, nIndexCount, desc ); |
|
|
|
// This is taken care of in the function above. |
|
// CBaseMeshDX8::m_bMeshLocked = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Renders a pass |
|
//----------------------------------------------------------------------------- |
|
|
|
void CBufferedMeshDX8::RenderPass() |
|
{ |
|
// this should never be called! |
|
Assert(0); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Flushes queued data |
|
//----------------------------------------------------------------------------- |
|
|
|
void CBufferedMeshDX8::Flush( ) |
|
{ |
|
// If you are hitting this assert you are causing a flush between a |
|
// meshbuilder begin/end and you are more than likely losing rendering data. |
|
AssertOnce( !CBaseMeshDX8::m_bMeshLocked ); |
|
|
|
if ( m_pMesh && !m_IsFlushing && m_FlushNeeded ) |
|
{ |
|
VPROF( "CBufferedMeshDX8::Flush" ); |
|
|
|
#ifdef DEBUG_BUFFERED_MESHES |
|
if( m_BufferedStateSet ) |
|
{ |
|
BufferedState_t compare; |
|
ShaderAPI()->GetBufferedState( compare ); |
|
Assert( !memcmp( &compare, &m_BufferedState, sizeof(compare) ) ); |
|
m_BufferedStateSet = false; |
|
} |
|
#endif |
|
|
|
m_IsFlushing = true; |
|
|
|
// Actually draws the data using the mesh's material |
|
static_cast<IMesh*>(m_pMesh)->Draw(); |
|
|
|
m_IsFlushing = false; |
|
m_FlushNeeded = false; |
|
|
|
m_pMesh->SetFlexMesh( NULL, 0 ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// |
|
// Mesh manager implementation |
|
// |
|
//----------------------------------------------------------------------------- |
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CMeshMgr::CMeshMgr() : |
|
m_pDynamicIndexBuffer(0), |
|
m_DynamicTempMesh(true), |
|
m_pVertexIDBuffer(0), |
|
m_pCurrentVertexBuffer( NULL ), |
|
m_CurrentVertexFormat( 0 ), |
|
m_pCurrentIndexBuffer( NULL ), |
|
m_DynamicIndexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, MATERIAL_INDEX_FORMAT_16BIT, INDEX_BUFFER_SIZE, "dynamic" ), |
|
m_DynamicVertexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, VERTEX_FORMAT_UNKNOWN, DYNAMIC_VERTEX_BUFFER_MEMORY, "dynamic" ) |
|
{ |
|
m_bUseFatVertices = false; |
|
m_nIndexBufferOffset = 0; |
|
memset( m_pVertexBufferOffset, 0, sizeof(m_pVertexBufferOffset) ); |
|
memset( m_pCurrentVertexStride, 0, sizeof(m_pCurrentVertexStride) ); |
|
memset( m_pFirstVertex, 0, sizeof(m_pFirstVertex) ); |
|
memset( m_pVertexCount, 0, sizeof(m_pVertexCount) ); |
|
m_nUnusedVertexFields = 0; |
|
m_nUnusedTextureCoords = 0; |
|
m_pZeroVertexBuffer = NULL; |
|
} |
|
|
|
CMeshMgr::~CMeshMgr() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialize, shutdown |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::Init() |
|
{ |
|
m_DynamicMesh.Init( 0 ); |
|
m_DynamicFlexMesh.Init( 1 ); |
|
|
|
CreateDynamicIndexBuffer(); |
|
|
|
// If we're running in vs3.0, allocate a vertexID buffer |
|
CreateVertexIDBuffer(); |
|
|
|
CreateZeroVertexBuffer(); |
|
|
|
m_BufferedMode = !IsX360(); |
|
} |
|
|
|
void CMeshMgr::Shutdown() |
|
{ |
|
CleanUp(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Task switch... |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::ReleaseBuffers() |
|
{ |
|
if ( IsPC() && mat_debugalttab.GetBool() ) |
|
{ |
|
Warning( "mat_debugalttab: CMeshMgr::ReleaseBuffers\n" ); |
|
} |
|
|
|
CleanUp(); |
|
m_DynamicMesh.Reset( ); |
|
m_DynamicFlexMesh.Reset( ); |
|
} |
|
|
|
void CMeshMgr::RestoreBuffers() |
|
{ |
|
if ( IsPC() && mat_debugalttab.GetBool() ) |
|
{ |
|
Warning( "mat_debugalttab: CMeshMgr::RestoreBuffers\n" ); |
|
} |
|
Init(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Cleans up vertex and index buffers |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::CleanUp() |
|
{ |
|
DestroyDynamicIndexBuffer(); |
|
|
|
DestroyVertexBuffers(); |
|
|
|
// If we're running in vs3.0, allocate a vertexID buffer |
|
DestroyVertexIDBuffer(); |
|
DestroyZeroVertexBuffer(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Fills a vertexID buffer |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount ) |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
// Fill the buffer with the values 0->(nCount-1) |
|
int nBaseVertexIndex = 0; |
|
float *pBuffer = (float*)pVertexIDBuffer->Lock( nCount, nBaseVertexIndex ); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
*pBuffer++ = (float)i; |
|
} |
|
pVertexIDBuffer->Unlock( nCount ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates, destroys the dynamic index buffer |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::CreateDynamicIndexBuffer() |
|
{ |
|
DestroyDynamicIndexBuffer(); |
|
SafeAssign( &m_pDynamicIndexBuffer, new CIndexBuffer( Dx9Device(), INDEX_BUFFER_SIZE, ShaderAPI()->UsingSoftwareVertexProcessing(), true ) ); |
|
} |
|
|
|
void CMeshMgr::DestroyDynamicIndexBuffer() |
|
{ |
|
SafeRelease( &m_pDynamicIndexBuffer ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates, destroys the vertexID buffer |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::CreateVertexIDBuffer() |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
DestroyVertexIDBuffer(); |
|
|
|
// Track mesh allocations |
|
g_VBAllocTracker->TrackMeshAllocations( "CreateVertexIDBuffer" ); |
|
if ( g_pHardwareConfig->HasFastVertexTextures() ) |
|
{ |
|
m_pVertexIDBuffer = new CVertexBuffer( Dx9Device(), 0, 0, sizeof(float), |
|
VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() ); |
|
FillVertexIDBuffer( m_pVertexIDBuffer, VERTEX_BUFFER_SIZE ); |
|
} |
|
g_VBAllocTracker->TrackMeshAllocations( NULL ); |
|
} |
|
|
|
void CMeshMgr::CreateZeroVertexBuffer() |
|
{ |
|
if ( !m_pZeroVertexBuffer ) |
|
{ |
|
// In GL glVertexAttribPointer() doesn't support strides of 0, so we need to allocate a dummy vertex buffer large enough to handle 16-bit indices with a stride of 4 byte per vertex, plus a bit more for safety (in case basevertexindex is > 0). |
|
// We could also try just disabling any vertex attribs that fetch from stream 2 and need 0's, but AMD reports this could hit a slow path in the driver. Argh. |
|
uint nBufSize = IsOpenGL() ? ( 65536 * 2 * 4 ) : 4096; |
|
HRESULT hr = Dx9Device()->CreateVertexBuffer( nBufSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_pZeroVertexBuffer, NULL ); |
|
if ( !FAILED( hr ) ) |
|
{ |
|
void *pData = NULL; |
|
m_pZeroVertexBuffer->Lock( 0, nBufSize, &pData, D3DLOCK_NOSYSLOCK ); |
|
if ( pData ) |
|
{ |
|
V_memset( pData, 0, nBufSize ); |
|
m_pZeroVertexBuffer->Unlock(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void CMeshMgr::DestroyZeroVertexBuffer() |
|
{ |
|
if ( m_pZeroVertexBuffer ) |
|
{ |
|
m_pZeroVertexBuffer->Release(); |
|
m_pZeroVertexBuffer = NULL; |
|
} |
|
} |
|
|
|
void CMeshMgr::DestroyVertexIDBuffer() |
|
{ |
|
if ( m_pVertexIDBuffer ) |
|
{ |
|
delete m_pVertexIDBuffer; |
|
m_pVertexIDBuffer = NULL; |
|
} |
|
} |
|
|
|
CVertexBuffer *CMeshMgr::GetVertexIDBuffer( ) |
|
{ |
|
return m_pVertexIDBuffer; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Unused vertex fields |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords ) |
|
{ |
|
m_nUnusedVertexFields = nFlags; |
|
m_nUnusedTextureCoords = 0; |
|
for ( int i = 0; i < nTexCoordCount; ++i ) |
|
{ |
|
if ( pUnusedTexCoords[i] ) |
|
{ |
|
m_nUnusedTextureCoords |= ( 1 << i ); |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Is the mesh dynamic? |
|
//----------------------------------------------------------------------------- |
|
bool CMeshMgr::IsDynamicMesh( IMesh* pMesh ) const |
|
{ |
|
return ( pMesh == &m_DynamicMesh ) || ( pMesh == &m_DynamicFlexMesh ); |
|
} |
|
|
|
bool CMeshMgr::IsBufferedDynamicMesh( IMesh* pMesh ) const |
|
{ |
|
return ( pMesh == &m_BufferedMesh ); |
|
} |
|
|
|
bool CMeshMgr::IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const |
|
{ |
|
return ( pVertexBuffer == &m_DynamicVertexBuffer ); |
|
} |
|
|
|
bool CMeshMgr::IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const |
|
{ |
|
return ( pIndexBuffer == &m_DynamicIndexBuffer ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Discards the dynamic vertex and index buffer |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::DiscardVertexBuffers() |
|
{ |
|
VPROF_BUDGET( "CMeshMgr::DiscardVertexBuffers", VPROF_BUDGETGROUP_SWAP_BUFFERS ); |
|
// This shouldn't be necessary, but it seems to be on GeForce 2 |
|
// It helps when running WC and the engine simultaneously. |
|
ResetMeshRenderState(); |
|
|
|
if ( !g_pShaderDeviceDx8->IsDeactivated() ) |
|
{ |
|
for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; ) |
|
{ |
|
m_DynamicVertexBuffers[i].m_pBuffer->FlushAtFrameStart(); |
|
} |
|
m_pDynamicIndexBuffer->FlushAtFrameStart(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Releases all dynamic vertex buffers |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::DestroyVertexBuffers() |
|
{ |
|
// Necessary for cleanup |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( 0 ); |
|
D3DSetStreamSource( 0, 0, 0, 0 ); |
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); |
|
RECORD_INT( 1 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( 0 ); |
|
D3DSetStreamSource( 1, 0, 0, 0 ); |
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); |
|
RECORD_INT( 2 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( 0 ); |
|
D3DSetStreamSource( 2, 0, 0, 0 ); |
|
|
|
#ifndef _X360 |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); |
|
RECORD_INT( 3 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( 0 ); |
|
D3DSetStreamSource( 3, 0, 0, 0 ); |
|
#endif |
|
|
|
for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; ) |
|
{ |
|
if (m_DynamicVertexBuffers[i].m_pBuffer) |
|
{ |
|
delete m_DynamicVertexBuffers[i].m_pBuffer; |
|
} |
|
} |
|
m_DynamicVertexBuffers.RemoveAll(); |
|
m_DynamicMesh.Reset(); |
|
m_DynamicFlexMesh.Reset(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Flushes the dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::Flush() |
|
{ |
|
if ( IsPC() ) |
|
{ |
|
m_BufferedMesh.HandleLateCreation(); |
|
m_BufferedMesh.Flush(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates, destroys static meshes |
|
//----------------------------------------------------------------------------- |
|
IMesh* CMeshMgr::CreateStaticMesh( VertexFormat_t format, const char *pTextureBudgetGroup, IMaterial * pMaterial ) |
|
{ |
|
// FIXME: Use a fixed-size allocator |
|
CMeshDX8* pNewMesh = new CMeshDX8( pTextureBudgetGroup ); |
|
pNewMesh->SetVertexFormat( format ); |
|
if ( pMaterial != NULL ) |
|
{ |
|
pNewMesh->SetMorphFormat( pMaterial->GetMorphFormat() ); |
|
pNewMesh->SetMaterial( pMaterial ); |
|
} |
|
return pNewMesh; |
|
} |
|
|
|
void CMeshMgr::DestroyStaticMesh( IMesh* pMesh ) |
|
{ |
|
// Don't destroy the dynamic mesh! |
|
Assert( !IsDynamicMesh( pMesh ) ); |
|
CBaseMeshDX8* pMeshImp = static_cast<CBaseMeshDX8*>(pMesh); |
|
if (pMeshImp) |
|
{ |
|
delete pMeshImp; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets at the *real* dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
IMesh* CMeshMgr::GetActualDynamicMesh( VertexFormat_t format ) |
|
{ |
|
m_DynamicMesh.SetVertexFormat( format ); |
|
return &m_DynamicMesh; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Copy a static mesh index buffer to a dynamic mesh index buffer |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, |
|
CMeshDX8 *pSrcIndexMesh ) |
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ ); |
|
|
|
Assert( !pSrcIndexMesh->IsDynamic() ); |
|
int nIndexCount = pSrcIndexMesh->IndexCount(); |
|
|
|
CMeshBuilder dstMeshBuilder; |
|
dstMeshBuilder.Begin( pDstIndexMesh, pSrcIndexMesh->GetPrimitiveType(), 0, nIndexCount ); |
|
CIndexBuffer *srcIndexBuffer = pSrcIndexMesh->GetIndexBuffer(); |
|
int dummy = 0; |
|
unsigned short *srcIndexArray = srcIndexBuffer->Lock( false, nIndexCount, dummy, 0 ); |
|
int i; |
|
for( i = 0; i < nIndexCount; i++ ) |
|
{ |
|
dstMeshBuilder.Index( srcIndexArray[i] ); |
|
dstMeshBuilder.AdvanceIndex(); |
|
} |
|
srcIndexBuffer->Unlock( 0 ); |
|
dstMeshBuilder.End(); |
|
} |
|
|
|
|
|
IMesh *CMeshMgr::GetFlexMesh() |
|
{ |
|
if ( g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_b() ) |
|
{ |
|
// FIXME: Kinda ugly size.. 28 bytes |
|
m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_WRINKLE | VERTEX_FORMAT_USE_EXACT_FORMAT ); |
|
} |
|
else |
|
{ |
|
// Same size as a pair of float3s (24 bytes) |
|
m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_USE_EXACT_FORMAT ); |
|
} |
|
return &m_DynamicFlexMesh; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets at the dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
IMesh* CMeshMgr::GetDynamicMesh( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, |
|
bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride ) |
|
{ |
|
tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ ); |
|
|
|
Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); |
|
|
|
if ( IsX360() ) |
|
{ |
|
buffered = false; |
|
} |
|
|
|
// Can't be buffered if we're overriding the buffers |
|
if ( pVertexOverride || pIndexOverride ) |
|
{ |
|
buffered = false; |
|
} |
|
|
|
// When going from buffered to unbuffered mode, need to flush.. |
|
if ((m_BufferedMode != buffered) && m_BufferedMode) |
|
{ |
|
m_BufferedMesh.SetMesh(0); |
|
} |
|
m_BufferedMode = buffered; |
|
|
|
IMaterialInternal* pMatInternal = static_cast<IMaterialInternal*>(pMaterial); |
|
|
|
bool needTempMesh = ShaderAPI()->IsInSelectionMode(); |
|
|
|
#ifdef DRAW_SELECTION |
|
if( g_bDrawSelection ) |
|
{ |
|
needTempMesh = true; |
|
} |
|
#endif |
|
|
|
CBaseMeshDX8* pMesh; |
|
|
|
if ( needTempMesh ) |
|
{ |
|
// These haven't been implemented yet for temp meshes! |
|
// I'm not a hundred percent sure how to implement them; it would |
|
// involve a lock and a copy at least, which would stall the entire |
|
// rendering pipeline. |
|
Assert( !pVertexOverride ); |
|
|
|
if( pIndexOverride ) |
|
{ |
|
CopyStaticMeshIndexBufferToTempMeshIndexBuffer( &m_DynamicTempMesh, |
|
( CMeshDX8 * )pIndexOverride ); |
|
} |
|
pMesh = &m_DynamicTempMesh; |
|
} |
|
else |
|
{ |
|
pMesh = &m_DynamicMesh; |
|
} |
|
|
|
if ( m_BufferedMode ) |
|
{ |
|
Assert( !m_BufferedMesh.WasNotRendered() ); |
|
m_BufferedMesh.SetMesh( pMesh ); |
|
pMesh = &m_BufferedMesh; |
|
} |
|
|
|
if( !pVertexOverride ) |
|
{ |
|
// Remove VERTEX_FORMAT_COMPRESSED from the material's format (dynamic meshes don't |
|
// support compression, and all materials should support uncompressed verts too) |
|
VertexFormat_t materialFormat = pMatInternal->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED; |
|
VertexFormat_t fmt = ( vertexFormat != 0 ) ? vertexFormat : materialFormat; |
|
if ( vertexFormat != 0 ) |
|
{ |
|
int nVertexFormatBoneWeights = NumBoneWeights( vertexFormat ); |
|
if ( nHWSkinBoneCount < nVertexFormatBoneWeights ) |
|
{ |
|
nHWSkinBoneCount = nVertexFormatBoneWeights; |
|
} |
|
} |
|
|
|
// Force the requested number of bone weights |
|
fmt &= ~VERTEX_BONE_WEIGHT_MASK; |
|
if ( nHWSkinBoneCount > 0 ) |
|
{ |
|
fmt |= VERTEX_BONEWEIGHT( 2 ); |
|
fmt |= VERTEX_BONE_INDEX; |
|
} |
|
|
|
pMesh->SetVertexFormat( fmt ); |
|
} |
|
else |
|
{ |
|
CBaseMeshDX8 *pDX8Mesh = static_cast<CBaseMeshDX8*>(pVertexOverride); |
|
pMesh->SetVertexFormat( pDX8Mesh->GetVertexFormat() ); |
|
} |
|
pMesh->SetMorphFormat( pMatInternal->GetMorphFormat() ); |
|
pMesh->SetMaterial( pMatInternal ); |
|
|
|
// Note this works because we're guaranteed to not be using a buffered mesh |
|
// when we have overrides on |
|
// FIXME: Make work for temp meshes |
|
if ( pMesh == &m_DynamicMesh ) |
|
{ |
|
CBaseMeshDX8* pBaseVertex = static_cast<CBaseMeshDX8*>( pVertexOverride ); |
|
if ( pBaseVertex ) |
|
{ |
|
m_DynamicMesh.OverrideVertexBuffer( pBaseVertex->GetVertexBuffer() ); |
|
} |
|
|
|
CBaseMeshDX8* pBaseIndex = static_cast<CBaseMeshDX8*>( pIndexOverride ); |
|
if ( pBaseIndex ) |
|
{ |
|
m_DynamicMesh.OverrideIndexBuffer( pBaseIndex->GetIndexBuffer() ); |
|
} |
|
} |
|
|
|
return pMesh; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to construct vertex data |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::ComputeVertexDescription( unsigned char* pBuffer, |
|
VertexFormat_t vertexFormat, MeshDesc_t& desc ) const |
|
{ |
|
ComputeVertexDesc( pBuffer, vertexFormat, (VertexDesc_t &)desc ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes the vertex format |
|
//----------------------------------------------------------------------------- |
|
VertexFormat_t CMeshMgr::ComputeVertexFormat( unsigned int flags, |
|
int nTexCoordArraySize, int* pTexCoordDimensions, int numBoneWeights, |
|
int userDataSize ) const |
|
{ |
|
// Construct a bitfield that makes sense and is unique from the standard FVF formats |
|
VertexFormat_t fmt = flags & ~VERTEX_FORMAT_USE_EXACT_FORMAT; |
|
|
|
if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE ) |
|
{ |
|
// Vertex compression is disabled - make sure all materials |
|
// say "No!" to compressed verts ( tested in IsValidVertexFormat() ) |
|
fmt &= ~VERTEX_FORMAT_COMPRESSED; |
|
} |
|
|
|
// This'll take 3 bits at most |
|
Assert( numBoneWeights <= 4 ); |
|
|
|
if ( numBoneWeights > 0 ) |
|
{ |
|
fmt |= VERTEX_BONEWEIGHT( 2 ); // Always exactly two weights |
|
} |
|
|
|
// Size is measured in # of floats |
|
Assert( userDataSize <= 4 ); |
|
fmt |= VERTEX_USERDATA_SIZE(userDataSize); |
|
|
|
// NOTE: If pTexCoordDimensions isn't specified, then nTexCoordArraySize |
|
// is interpreted as meaning that we have n 2D texcoords in the first N texcoord slots |
|
nTexCoordArraySize = min( nTexCoordArraySize, (int)VERTEX_MAX_TEXTURE_COORDINATES ); |
|
for ( int i = 0; i < nTexCoordArraySize; ++i ) |
|
{ |
|
if ( pTexCoordDimensions ) |
|
{ |
|
Assert( pTexCoordDimensions[i] >= 0 && pTexCoordDimensions[i] <= 4 ); |
|
fmt |= VERTEX_TEXCOORD_SIZE( (TextureStage_t)i, pTexCoordDimensions[i] ); |
|
} |
|
else |
|
{ |
|
fmt |= VERTEX_TEXCOORD_SIZE( (TextureStage_t)i, 2 ); |
|
} |
|
} |
|
|
|
return fmt; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Use fat vertices (for tools) |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::UseFatVertices( bool bUseFat ) |
|
{ |
|
m_bUseFatVertices = bUseFat; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns the number of vertices we can render using the dynamic mesh |
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices ) |
|
{ |
|
CBaseMeshDX8 *pBaseMesh = static_cast<CBaseMeshDX8*>( pMesh ); |
|
if ( !pBaseMesh ) |
|
{ |
|
*pMaxVerts = 0; |
|
*pMaxIndices = m_pDynamicIndexBuffer->IndexCount(); |
|
return; |
|
} |
|
|
|
if ( IsBufferedDynamicMesh( pMesh ) ) |
|
{ |
|
pBaseMesh = (CBaseMeshDX8*)static_cast<CBufferedMeshDX8*>( pBaseMesh )->GetMesh(); |
|
pMesh = pBaseMesh; |
|
} |
|
|
|
// Static mesh? Max you can use is 65535 |
|
if ( !IsDynamicMesh( pMesh ) ) |
|
{ |
|
*pMaxVerts = 65535; |
|
*pMaxIndices = 65535; |
|
return; |
|
} |
|
|
|
CVertexBuffer *pVertexBuffer = pBaseMesh->GetVertexBuffer(); |
|
CIndexBuffer *pIndexBuffer = pBaseMesh->GetIndexBuffer(); |
|
|
|
if ( !pVertexBuffer ) |
|
{ |
|
*pMaxVerts = 0; |
|
*pMaxIndices = 0; |
|
return; |
|
} |
|
|
|
if ( !bMaxUntilFlush ) |
|
{ |
|
*pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize(); |
|
if ( *pMaxVerts > 65535 ) |
|
{ |
|
*pMaxVerts = 65535; |
|
} |
|
*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0; |
|
return; |
|
} |
|
|
|
*pMaxVerts = pVertexBuffer->NumVerticesUntilFlush(); |
|
*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() - pIndexBuffer->IndexPosition() : 0; |
|
if ( *pMaxVerts == 0 ) |
|
{ |
|
*pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize(); |
|
} |
|
if ( *pMaxVerts > 65535 ) |
|
{ |
|
*pMaxVerts = 65535; |
|
} |
|
if ( *pMaxIndices == 0 ) |
|
{ |
|
*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0; |
|
} |
|
} |
|
|
|
int CMeshMgr::GetMaxVerticesToRender( IMaterial *pMaterial ) |
|
{ |
|
Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() ); |
|
|
|
// Be conservative, assume no compression (in here, we don't know if the caller will used a compressed VB or not) |
|
// FIXME: allow the caller to specify which compression type should be used to compute size from the vertex format |
|
// (this can vary between multiple VBs/Meshes using the same material) |
|
VertexFormat_t fmt = pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED; |
|
int nVertexSize = VertexFormatSize( fmt ); |
|
if ( nVertexSize == 0 ) |
|
{ |
|
// unable to determine vertex format information, possibly due to device loss. |
|
Warning( "bad vertex size for material %s\n", pMaterial->GetName() ); |
|
return 0; |
|
} |
|
|
|
int nMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / nVertexSize; |
|
return MIN( nMaxVerts, 65535 ); |
|
} |
|
|
|
int CMeshMgr::GetMaxIndicesToRender( ) |
|
{ |
|
return INDEX_BUFFER_SIZE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns a vertex buffer appropriate for the flags |
|
//----------------------------------------------------------------------------- |
|
CVertexBuffer *CMeshMgr::FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t vertexFormat ) |
|
{ |
|
int vertexSize = VertexFormatSize( vertexFormat ); |
|
|
|
while ( m_DynamicVertexBuffers.Count() <= nDynamicBufferId ) |
|
{ |
|
// Track VB allocations (override any prior allocator string set higher up on the callstack) |
|
g_VBAllocTracker->TrackMeshAllocations( NULL ); |
|
g_VBAllocTracker->TrackMeshAllocations( "CMeshMgr::FindOrCreateVertexBuffer (dynamic VB)" ); |
|
|
|
// create the single 1MB dynamic vb that will be shared amongst all consumers |
|
// the correct thing is to use the largest expected vertex format size of max elements, but this |
|
// creates an undesirably large buffer - instead create the buffer we want, and fix consumers that bork |
|
// NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions |
|
int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize(); |
|
int nIndex = m_DynamicVertexBuffers.AddToTail(); |
|
m_DynamicVertexBuffers[nIndex].m_VertexSize = 0; |
|
m_DynamicVertexBuffers[nIndex].m_pBuffer = new CVertexBuffer( Dx9Device(), 0, 0, |
|
nBufferMemory / VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing(), true ); |
|
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL ); |
|
} |
|
|
|
if ( m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize != vertexSize ) |
|
{ |
|
// provide caller with dynamic vb in expected format |
|
// NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions |
|
int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize(); |
|
m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize = vertexSize; |
|
m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer->ChangeConfiguration( vertexSize, nBufferMemory ); |
|
|
|
// size changed means stream stride needs update |
|
// mark cached stream state as invalid to reset stream |
|
if ( nDynamicBufferId == 0 ) |
|
{ |
|
g_pLastVertex = NULL; |
|
} |
|
} |
|
|
|
return m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer; |
|
} |
|
|
|
CIndexBuffer *CMeshMgr::GetDynamicIndexBuffer() |
|
{ |
|
return m_pDynamicIndexBuffer; |
|
} |
|
|
|
IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered ) |
|
{ |
|
Assert( 0 ); |
|
return NULL; |
|
// return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL ); |
|
} |
|
|
|
IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer( IMaterial *pMaterial, bool buffered ) |
|
{ |
|
Assert( 0 ); |
|
return NULL; |
|
// return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
IVertexBuffer *CMeshMgr::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup ) |
|
{ |
|
// FIXME: Use a fixed-size allocator |
|
CVertexBufferDx8 *pNewVertexBuffer = new CVertexBufferDx8( type, fmt, nVertexCount, pBudgetGroup ); |
|
return pNewVertexBuffer; |
|
} |
|
|
|
IIndexBuffer *CMeshMgr::CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup ) |
|
{ |
|
switch( bufferType ) |
|
{ |
|
case SHADER_BUFFER_TYPE_STATIC: |
|
case SHADER_BUFFER_TYPE_DYNAMIC: |
|
{ |
|
CIndexBufferDx8 *pIndexBuffer = new CIndexBufferDx8( bufferType, fmt, nIndexCount, pBudgetGroup ); |
|
return pIndexBuffer; |
|
} |
|
case SHADER_BUFFER_TYPE_STATIC_TEMP: |
|
case SHADER_BUFFER_TYPE_DYNAMIC_TEMP: |
|
Assert( 0 ); |
|
return NULL; |
|
default: |
|
Assert( 0 ); |
|
return NULL; |
|
} |
|
} |
|
|
|
void CMeshMgr::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer ) |
|
{ |
|
if ( pVertexBuffer && !IsDynamicVertexBuffer( pVertexBuffer ) ) |
|
{ |
|
delete pVertexBuffer; |
|
} |
|
} |
|
|
|
void CMeshMgr::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer ) |
|
{ |
|
if ( pIndexBuffer && !IsDynamicIndexBuffer( pIndexBuffer ) ) |
|
{ |
|
delete pIndexBuffer; |
|
} |
|
} |
|
|
|
// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams? |
|
IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
bBuffered = false; |
|
} |
|
|
|
if ( CompressionType( vertexFormat ) != VERTEX_COMPRESSION_NONE ) |
|
{ |
|
// UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing) |
|
DebuggerBreak(); |
|
return NULL; |
|
} |
|
|
|
// MESHFIXME |
|
#if 0 |
|
if ( ( m_BufferedMode != bBuffered ) && m_BufferedMode ) |
|
{ |
|
m_BufferedIndexBuffer.SetIndexBuffer( NULL ); |
|
} |
|
#endif |
|
|
|
m_BufferedMode = bBuffered; |
|
Assert( !m_BufferedMode ); // MESHFIXME: don't deal with buffered VBs yet. |
|
|
|
bool needTempMesh = ShaderAPI()->IsInSelectionMode(); |
|
|
|
#ifdef DRAW_SELECTION |
|
if( g_bDrawSelection ) |
|
{ |
|
needTempMesh = true; |
|
} |
|
#endif |
|
|
|
Assert( !needTempMesh ); // MESHFIXME: don't support temp meshes here yet. |
|
|
|
CVertexBufferDx8 *pVertexBuffer; |
|
|
|
if ( needTempMesh ) |
|
{ |
|
Assert( 0 ); // MESHFIXME: don't do this yet. |
|
// pVertexBuffer = &m_DynamicTempVertexBuffer; |
|
pVertexBuffer = NULL; |
|
} |
|
else |
|
{ |
|
pVertexBuffer = &m_DynamicVertexBuffer; |
|
} |
|
|
|
if ( m_BufferedMode ) |
|
{ |
|
Assert( 0 ); // don't support this yet. |
|
#if 0 |
|
Assert( !m_BufferedMesh.WasNotRendered() ); |
|
m_BufferedMesh.SetMesh( pMesh ); |
|
pMesh = &m_BufferedMesh; |
|
#endif |
|
} |
|
|
|
return pVertexBuffer; |
|
} |
|
|
|
IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer( MaterialIndexFormat_t fmt, bool bBuffered ) |
|
{ |
|
if ( IsX360() ) |
|
{ |
|
bBuffered = false; |
|
} |
|
|
|
m_BufferedMode = bBuffered; |
|
|
|
Assert( !m_BufferedMode ); |
|
|
|
#ifdef DBGFLAG_ASSERT |
|
bool needTempMesh = |
|
#endif |
|
ShaderAPI()->IsInSelectionMode(); |
|
|
|
#ifdef DRAW_SELECTION |
|
if( g_bDrawSelection ) |
|
{ |
|
needTempMesh = true; |
|
} |
|
#endif |
|
|
|
Assert( !needTempMesh ); // don't handle this yet. MESHFIXME |
|
|
|
CIndexBufferBase *pIndexBuffer = &m_DynamicIndexBuffer; |
|
return pIndexBuffer; |
|
} |
|
|
|
void CMeshMgr::SetVertexIDStreamState() |
|
{ |
|
if ( IsX360() ) |
|
return; |
|
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces. |
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces. |
|
bool bUsingVertexID = false;//IsUsingVertexID(); |
|
// if ( bUsingVertexID != g_bUsingVertexID ) |
|
{ |
|
if ( bUsingVertexID ) |
|
{ |
|
// NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH |
|
// It's because the indices (which are not 0 based for dynamic buffers) |
|
// are accessing both the vertexID buffer + the regular vertex buffer. |
|
// This *might* be fixable with baseVertexIndex? |
|
Assert( !m_pCurrentVertexBuffer->IsDynamic() ); |
|
|
|
CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( ); |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( pVertexIDBuffer->UID() ); |
|
RECORD_INT( 3 ); |
|
RECORD_INT( 0 ); |
|
RECORD_INT( pVertexIDBuffer->VertexSize() ); |
|
|
|
D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() ); |
|
pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
} |
|
else |
|
{ |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); // vertex buffer id |
|
RECORD_INT( 3 ); // stream |
|
RECORD_INT( 0 ); // vertex offset |
|
RECORD_INT( 0 ); // vertex size |
|
|
|
D3DSetStreamSource( 3, 0, 0, 0 ); |
|
} |
|
g_bUsingVertexID = bUsingVertexID; |
|
} |
|
} |
|
|
|
void CMeshMgr::SetColorStreamState() |
|
{ |
|
if ( g_pLastColorMesh ) |
|
{ |
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 ); |
|
RECORD_INT( -1 ); // vertex buffer id |
|
RECORD_INT( 1 ); // stream |
|
RECORD_INT( 0 ); // vertex offset |
|
RECORD_INT( 0 ); // vertex size |
|
|
|
D3DSetStreamSource( 1, 0, 0, 0 ); |
|
} |
|
g_pLastColorMesh = NULL; |
|
g_nLastColorMeshVertOffsetInBytes = 0; |
|
} |
|
|
|
void CMeshMgr::SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride ) |
|
{ |
|
// Calls in here assume shader support... |
|
if ( HardwareConfig()->SupportsVertexAndPixelShaders() ) |
|
{ |
|
// Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 0 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia) |
|
// togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency. |
|
D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 ); |
|
|
|
// cFlexScale.x masks flex in vertex shader |
|
if ( g_pHardwareConfig->Caps().m_SupportsVertexShaders_2_0 ) |
|
{ |
|
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; |
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 ); |
|
} |
|
|
|
g_bFlexMeshStreamSet = false; |
|
} |
|
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces. |
|
if ( g_pLastVertex || ( g_pLastVertexBuffer != m_pCurrentVertexBuffer->GetDx9Buffer() ) || |
|
( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) || ( g_nLastVertStride != nVertexStride )) |
|
{ |
|
Assert( m_pCurrentVertexBuffer && m_pCurrentVertexBuffer->GetDx9Buffer() ); |
|
|
|
D3DSetStreamSource( 0, m_pCurrentVertexBuffer->GetDx9Buffer(), nVertOffsetInBytes, nVertexStride ); |
|
m_pCurrentVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
|
|
g_pLastVertex = NULL; |
|
g_nLastVertStride = nVertexStride; |
|
g_pLastVertexBuffer = m_pCurrentVertexBuffer->GetDx9Buffer(); |
|
g_nLastVertOffsetInBytes = nVertOffsetInBytes; |
|
} |
|
} |
|
|
|
bool CMeshMgr::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride ) |
|
{ |
|
// Can't set the state if we're deactivated |
|
if ( g_pShaderDeviceDx8->IsDeactivated() ) |
|
{ |
|
ResetMeshRenderState(); |
|
return false; |
|
} |
|
|
|
// make sure the vertex format is a superset of the current material's |
|
// vertex format... |
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces. |
|
#if 0 |
|
// FIXME |
|
if ( !IsValidVertexFormat( vertexFormat ) ) |
|
{ |
|
Warning( "Material %s is being applied to a model, you need $model=1 in the .vmt file!\n", |
|
ShaderAPI()->GetBoundMaterial()->GetName() ); |
|
return false; |
|
} |
|
#endif |
|
|
|
SetVertexIDStreamState(); |
|
SetColorStreamState(); |
|
SetVertexStreamState( nVertexOffsetInBytes, nVertexStride ); |
|
SetIndexStreamState( nFirstVertexIdx ); |
|
|
|
return true; |
|
} |
|
|
|
void CMeshMgr::BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions ) |
|
{ |
|
// FIXME: Multiple stream support isn't implemented yet |
|
Assert( nStreamID == 0 ); |
|
|
|
m_pCurrentVertexBuffer = static_cast< CVertexBufferDx8 * >( pVertexBuffer ); |
|
m_CurrentVertexFormat = fmt; |
|
m_pVertexBufferOffset[nStreamID] = nOffsetInBytes; |
|
m_pCurrentVertexStride[nStreamID] = m_pCurrentVertexBuffer->VertexSize(); |
|
m_pFirstVertex[nStreamID] = nFirstVertex; |
|
m_pVertexCount[nStreamID] = nVertexCount, |
|
m_pVertexIDBuffer = NULL; |
|
} |
|
|
|
void CMeshMgr::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes ) |
|
{ |
|
m_pCurrentIndexBuffer = static_cast< CIndexBufferBase * >( pIndexBuffer ); |
|
m_nIndexBufferOffset = nOffsetInBytes; |
|
} |
|
|
|
void CMeshMgr::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount ) |
|
{ |
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces. |
|
// make sure we aren't using a morph stream for this path. |
|
// Assert( !IsUsingMorphData() ); |
|
// Assert( !m_pColorMesh ); |
|
|
|
SetRenderState( m_pVertexBufferOffset[0], /* nFirstVertexIdx */0, m_CurrentVertexFormat, m_pCurrentVertexStride[0] ); |
|
|
|
m_PrimitiveType = MATERIAL_TRIANGLES; |
|
Assert( primitiveType == MATERIAL_TRIANGLES ); |
|
|
|
m_nFirstIndex = nFirstIndex; |
|
m_nNumIndices = nIndexCount; |
|
|
|
ShaderAPI()->DrawWithVertexAndIndexBuffers(); |
|
} |
|
|
|
void CMeshMgr::RenderPassWithVertexAndIndexBuffers( void ) |
|
{ |
|
// LOCK_SHADERAPI(); MESHFIXME |
|
VPROF( "CShaderAPIDX8::RenderPassWithVertexAndIndexBuffers" ); |
|
|
|
Assert( m_PrimitiveType != MATERIAL_HETEROGENOUS ); |
|
|
|
// for ( int iPrim=0; iPrim < s_nPrims; iPrim++ ) |
|
{ |
|
// CPrimList *pPrim = &s_pPrims[iPrim]; |
|
|
|
// if ( pPrim->m_NumIndices == 0 ) |
|
// continue; |
|
|
|
if ( m_PrimitiveType == MATERIAL_POINTS ) |
|
{ |
|
// (For point lists, we don't actually fill in indices, but we treat it as |
|
// though there are indices for the list up until here). |
|
Assert( 0 ); |
|
// Dx9Device()->DrawPrimitive( ComputeMode( m_PrimitiveType ), s_FirstVertex, pPrim->m_NumIndices ); |
|
} |
|
else |
|
{ |
|
// int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices ); |
|
|
|
// Warning( "CMeshMgr::RenderPassWithVertexAndIndexBuffers: DrawIndexedPrimitive: m_nFirstIndex = %d numPrimitives = %d\n", ( int )( ( CDynamiCIndexBufferDx8 * )m_pCurrentIndexBuffer )->m_FirstIndex, ( int )( m_nNumIndices / 3 ) ); |
|
{ |
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" ); |
|
// VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 ); |
|
// VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives ); |
|
|
|
// Dx9Device()->DrawIndexedPrimitive( |
|
// m_Mode, |
|
// m_FirstIndex, |
|
// s_FirstVertex, |
|
// s_NumVertices, |
|
// pPrim->m_FirstIndex, |
|
// numPrimitives ); |
|
|
|
Assert( m_nFirstIndex >= 0 ); |
|
|
|
#ifdef CHECK_INDICES |
|
// g_pLastVertex - this is the current vertex buffer |
|
// g_pLastColorMesh - this is the curent color mesh, if there is one. |
|
// g_pLastIndex - this is the current index buffer. |
|
// vertoffset : m_FirstIndex |
|
CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8 * >( m_pCurrentIndexBuffer ); |
|
if( m_PrimitiveType == MATERIAL_TRIANGLES || m_PrimitiveType == MATERIAL_TRIANGLE_STRIP ) |
|
{ |
|
// FIXME: need to be able to deal with multiple stream here, but don't bother for now. |
|
int j; |
|
int numVerts = m_pVertexCount[0]; |
|
for( j = 0; j < m_nNumIndices; j++ ) |
|
{ |
|
int index = pIndexBuffer->GetShadowIndex( j + m_nFirstIndex ); |
|
Assert( index >= m_pFirstVertex[0] ); |
|
Assert( index < m_pFirstVertex[0] + numVerts ); |
|
} |
|
} |
|
#endif // CHECK_INDICES |
|
Dx9Device()->DrawIndexedPrimitive( |
|
ComputeMode( m_PrimitiveType ), // Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method. |
|
|
|
/*m_FirstIndex*/ 0, // Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer. |
|
|
|
/*s_FirstVertex*/ m_pFirstVertex[0],// Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex. |
|
// This is zero for now since we don't do more than one batch yet with the new mesh interface. |
|
|
|
/*s_NumVertices*/ m_pVertexCount[0], |
|
// Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex. |
|
// This is simple the number of verts in the current vertex buffer for now since we don't do more than one batch with the new mesh interface. |
|
|
|
m_nFirstIndex /*pPrim->m_FirstIndex*/, // Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer. |
|
|
|
m_nNumIndices / 3/*numPrimitives*/ // Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type. |
|
); |
|
|
|
Assert( CMeshDX8::s_FirstVertex == 0 ); |
|
Assert( CMeshDX8::s_NumVertices == 0 ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void CMeshMgr::SetIndexStreamState( int firstVertexIdx ) |
|
{ |
|
CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8* >( m_pCurrentIndexBuffer ); |
|
IDirect3DIndexBuffer9 *pDx9Buffer = pIndexBuffer ? pIndexBuffer->GetDx9Buffer() : NULL; |
|
if ( g_pLastIndex || g_pLastIndexBuffer != pDx9Buffer ) |
|
{ |
|
Dx9Device()->SetIndices( pDx9Buffer ); |
|
pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() ); |
|
|
|
g_pLastIndexBuffer = pDx9Buffer; |
|
SafeRelease( &g_pLastIndex ); |
|
g_LastVertexIdx = -1; |
|
} |
|
} |
|
|
|
|
|
|