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.
6931 lines
209 KiB
6931 lines
209 KiB
//===== Copyright (c) Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//==================================================================//
|
|
|
|
#include "locald3dtypes.h"
|
|
#include "imeshdx8.h"
|
|
#include "shaderapidx8_global.h"
|
|
#include "materialsystem/IShader.h"
|
|
#include "tier0/vprof.h"
|
|
#include "studio.h"
|
|
#include "tier1/fmtstr.h"
|
|
|
|
#include "tier0/platform.h"
|
|
#include "tier0/systeminformation.h"
|
|
|
|
#include "smartptr.h"
|
|
|
|
// fixme - stick this in a header file.
|
|
#if defined( _DEBUG ) && !defined( _GAMECONSOLE )
|
|
// define this if you want to range check all indices when drawing
|
|
#define CHECK_INDICES
|
|
#endif
|
|
#ifdef CHECK_INDICES
|
|
#define CHECK_INDICES_MAX_NUM_STREAMS 2
|
|
#endif
|
|
|
|
#include "dynamicib.h"
|
|
#include "dynamicvb.h"
|
|
#include "utlvector.h"
|
|
#include "shaderapi/ishaderapi.h"
|
|
#include "imaterialinternal.h"
|
|
#include "imaterialsysteminternal.h"
|
|
#include "shaderapidx8.h"
|
|
#include "shaderapi/ishaderutil.h"
|
|
#include "materialsystem/imaterialsystemhardwareconfig.h"
|
|
#include "materialsystem/materialsystem_config.h"
|
|
#include "materialsystem/ivballoctracker.h"
|
|
#include "tier1/strtools.h"
|
|
#include "convar.h"
|
|
#include "shaderdevicedx8.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#ifdef _GAMECONSOLE
|
|
|
|
#define MAX_TEMP_BUFFER 3
|
|
static int s_nMemoryFrame;
|
|
static CMemoryStack s_BufferMemory[MAX_TEMP_BUFFER];
|
|
|
|
void *AllocateTempBuffer( size_t nSizeInBytes )
|
|
{
|
|
return s_BufferMemory[s_nMemoryFrame].Alloc( nSizeInBytes, true );
|
|
}
|
|
|
|
#endif // _GAMECONSOLE
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void FailedLock( const char *pszMsg )
|
|
{
|
|
if ( IsPC() )
|
|
{
|
|
Error( "%s", pszMsg );
|
|
}
|
|
else
|
|
{
|
|
Warning( "%s", pszMsg );
|
|
}
|
|
g_pMemAlloc->OutOfMemory();
|
|
}
|
|
|
|
#define MAX_DX8_STREAMS 16
|
|
|
|
#define VERTEX_FORMAT_INVALID 0xFFFFFFFFFFFFFFFFull
|
|
|
|
// this is hooked into the engines convar
|
|
extern ConVar mat_debugalttab;
|
|
|
|
//#define DRAW_SELECTION 1
|
|
static bool g_bDrawSelection = true; // only used in DRAW_SELECTION
|
|
|
|
// NOTE: Using 6 here since we don't want extra checks in CIndexBuilder::FastQuad
|
|
static unsigned int g_pScratchIndexBuffer[6]; // shove indices into this if you don't actually want indices
|
|
|
|
// used to hold instance data when drawing multiple instances
|
|
static const MeshInstanceData_t *g_pInstanceData = NULL;
|
|
static CompiledLightingState_t *g_pInstanceCompiledState = NULL;
|
|
static InstanceInfo_t *g_pInstanceInfo = NULL;
|
|
static int g_nInstanceCount = 0;
|
|
#ifdef _DEBUG
|
|
int CVertexBuffer::s_BufferCount = 0;
|
|
int CIndexBuffer::s_BufferCount = 0;
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Important enumerations
|
|
//-----------------------------------------------------------------------------
|
|
enum
|
|
{
|
|
VERTEX_BUFFER_SIZE = 32768,
|
|
MAX_QUAD_INDICES = 16384,
|
|
MAX_TESS_DIVISIONS_PER_SIDE = 16, // We can put any arbitrary number here, but we should tie it to the max that HW tessellators can do
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Code related to vertex buffers start here
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
class CVertexBufferDx8 : public CVertexBufferBase
|
|
{
|
|
typedef CVertexBufferBase BaseClass;
|
|
|
|
// Methods of IVertexBuffer
|
|
public:
|
|
virtual int VertexCount() const;
|
|
virtual VertexFormat_t GetVertexFormat() const;
|
|
virtual bool IsDynamic() const;
|
|
virtual void BeginCastBuffer( VertexFormat_t format );
|
|
virtual void EndCastBuffer( );
|
|
virtual int GetRoomRemaining() const;
|
|
virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc );
|
|
virtual void Unlock( int nVertexCount, VertexDesc_t &desc );
|
|
|
|
public:
|
|
// constructor
|
|
CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName );
|
|
virtual ~CVertexBufferDx8();
|
|
|
|
// Allocates, deallocates the index buffer
|
|
bool Allocate( );
|
|
void Free();
|
|
|
|
// Returns the vertex size
|
|
int VertexSize() const;
|
|
|
|
// Only used by dynamic buffers, indicates the next lock should perform a discard.
|
|
void Flush();
|
|
|
|
// Returns the D3D buffer
|
|
IDirect3DVertexBuffer9* GetDx9Buffer();
|
|
|
|
// Used to measure how much static buffer memory is touched each frame
|
|
void HandlePerFrameTextureStats( int nFrame );
|
|
|
|
protected:
|
|
IDirect3DVertexBuffer9 *m_pVertexBuffer;
|
|
VertexFormat_t m_VertexFormat;
|
|
int m_nVertexCount;
|
|
int m_nBufferSize;
|
|
int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite)
|
|
|
|
// Is it locked?
|
|
bool m_bIsLocked : 1;
|
|
bool m_bIsDynamic : 1;
|
|
bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time
|
|
|
|
#ifdef VPROF_ENABLED
|
|
int m_nVProfFrame;
|
|
int *m_pFrameCounter;
|
|
int *m_pGlobalCounter;
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
static int s_nBufferCount;
|
|
#endif
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Code related to index buffers start here
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
class CIndexBufferDx8 : public CIndexBufferBase
|
|
{
|
|
typedef CIndexBufferBase BaseClass;
|
|
|
|
// Methods of IIndexBuffer
|
|
public:
|
|
virtual int IndexCount( ) const;
|
|
virtual MaterialIndexFormat_t IndexFormat() const;
|
|
virtual int GetRoomRemaining() const;
|
|
virtual bool Lock( int nIndexCount, bool bAppend, IndexDesc_t &desc );
|
|
virtual void Unlock( int nIndexCount, IndexDesc_t &desc );
|
|
virtual void BeginCastBuffer( MaterialIndexFormat_t format );
|
|
virtual void EndCastBuffer( );
|
|
virtual bool IsDynamic() const;
|
|
virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); }
|
|
virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); }
|
|
|
|
public:
|
|
// constructor
|
|
CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName );
|
|
virtual ~CIndexBufferDx8();
|
|
|
|
// Allocates, deallocates the index buffer
|
|
bool Allocate( );
|
|
void Free();
|
|
|
|
// Returns the index size
|
|
int IndexSize() const;
|
|
|
|
// Only used by dynamic buffers, indicates the next lock should perform a discard.
|
|
void Flush();
|
|
|
|
// Returns the D3D buffer
|
|
IDirect3DIndexBuffer9* GetDx9Buffer();
|
|
|
|
// Used to measure how much static buffer memory is touched each frame
|
|
void HandlePerFrameTextureStats( int nFrame );
|
|
|
|
void SetIndexStreamState( int nFirstVertexIdx );
|
|
|
|
virtual bool IsExternal() const { return false; }
|
|
|
|
#ifdef CHECK_INDICES
|
|
unsigned short GetShadowIndex( int i ) const;
|
|
#endif
|
|
|
|
IDirect3DIndexBuffer9 *m_pIndexBuffer;
|
|
|
|
private:
|
|
|
|
MaterialIndexFormat_t m_IndexFormat;
|
|
int m_nIndexCount;
|
|
int m_nBufferSize;
|
|
int m_nFirstUnwrittenOffset; // Used only for dynamic buffers, indicates where it's safe to write (nooverwrite)
|
|
|
|
// Is it locked?
|
|
bool m_bIsLocked : 1;
|
|
bool m_bIsDynamic : 1;
|
|
bool m_bFlush : 1; // Used only for dynamic buffers, indicates to discard the next time
|
|
|
|
#ifdef CHECK_INDICES
|
|
unsigned char *m_pShadowIndices;
|
|
void *m_pLockIndexBuffer;
|
|
int m_nLockIndexBufferSize;
|
|
int m_nLockIndexOffset;
|
|
#endif
|
|
|
|
#ifdef VPROF_ENABLED
|
|
int m_nVProfFrame;
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
static int s_nBufferCount;
|
|
#endif
|
|
|
|
friend class CExternalIndexBufferDx8;
|
|
};
|
|
|
|
|
|
#ifdef _GAMECONSOLE
|
|
|
|
#include "tier0/memdbgoff.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// For externally allocated index buffers
|
|
//-----------------------------------------------------------------------------
|
|
class CExternalIndexBufferDx8 : public CIndexBufferDx8
|
|
{
|
|
typedef CIndexBufferDx8 BaseClass;
|
|
|
|
public:
|
|
// constructor
|
|
CExternalIndexBufferDx8( ) : BaseClass( SHADER_BUFFER_TYPE_STATIC, MATERIAL_INDEX_FORMAT_16BIT, 0, "external ib - ignore" )
|
|
{
|
|
}
|
|
|
|
virtual ~CExternalIndexBufferDx8()
|
|
{
|
|
if( IsPS3() )
|
|
{
|
|
m_pIndexBuffer = NULL; // we don't have to release the external dynamic IB
|
|
}
|
|
}
|
|
|
|
void Init( int nIndexCount, uint16 *pIndexData )
|
|
{
|
|
m_pIndexBuffer = CreateExternalDynamicIB( pIndexData, nIndexCount );
|
|
m_nBufferSize = m_nFirstUnwrittenOffset = nIndexCount * sizeof(uint16);
|
|
m_nIndexCount = nIndexCount;
|
|
}
|
|
|
|
|
|
virtual bool IsExternal() const { return true; }
|
|
};
|
|
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#endif // _GAMECONSOLE
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Backward compat mesh code; will go away soon
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
abstract_class CBaseMeshDX8 : public CMeshBase
|
|
{
|
|
public:
|
|
// constructor, destructor
|
|
CBaseMeshDX8();
|
|
virtual ~CBaseMeshDX8();
|
|
|
|
// FIXME: Make this work! Unsupported methods of IIndexBuffer + IVertexBuffer
|
|
virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t& desc ) { Assert(0); return false; }
|
|
virtual void Unlock( int nWrittenIndexCount, IndexDesc_t& desc ) { Assert(0); }
|
|
virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) { Assert(0); }
|
|
virtual void ModifyEnd( IndexDesc_t& desc ) { Assert(0); }
|
|
virtual void Spew( int nIndexCount, const IndexDesc_t & desc ) { Assert(0); }
|
|
virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) { Assert(0); }
|
|
virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) { Assert(0); return false; }
|
|
virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) { Assert(0); }
|
|
virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) { Assert(0); }
|
|
virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) { Assert(0); }
|
|
|
|
// Locks mesh for modifying
|
|
void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
|
|
void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
|
|
void ModifyEnd( MeshDesc_t& desc );
|
|
|
|
// Sets/gets the vertex format
|
|
virtual void SetVertexFormat( VertexFormat_t format, bool bHasVertexOverride, bool bHasIndexOverride );
|
|
virtual VertexFormat_t GetVertexFormat() const;
|
|
|
|
// Sets/gets the morph format
|
|
// Am I using morph data?
|
|
bool IsUsingVertexID() const
|
|
{
|
|
return ShaderAPI()->GetBoundMaterial()->IsUsingVertexID();
|
|
}
|
|
|
|
virtual bool IsExternal() const { return false; }
|
|
|
|
// Sets the material
|
|
virtual void SetMaterial( IMaterial* pMaterial );
|
|
|
|
// returns the # of vertices (static meshes only)
|
|
int VertexCount() const { return 0; }
|
|
|
|
void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes )
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
|
|
virtual void GetColorMesh( const IVertexBuffer** pMesh, int *pMeshVertexOffsetInBytes ) const
|
|
{
|
|
*pMesh = 0; *pMeshVertexOffsetInBytes = 0;
|
|
}
|
|
|
|
void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes )
|
|
{
|
|
Assert( pMesh == NULL && nVertexOffsetInBytes == 0 );
|
|
}
|
|
|
|
void DisableFlexMesh( )
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
|
|
void MarkAsDrawn() {}
|
|
|
|
bool HasColorMesh( ) const { return false; }
|
|
bool HasFlexMesh( ) const { return false; }
|
|
VertexStreamSpec_t *GetVertexStreamSpec() const { return NULL; }
|
|
|
|
// Draws the mesh
|
|
void DrawMesh( const Vector4D *pVecDiffuseModulation );
|
|
|
|
// Begins a pass
|
|
void BeginPass( );
|
|
|
|
// Spews the mesh data
|
|
virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t & desc );
|
|
|
|
// Call this in debug mode to make sure our data is good.
|
|
virtual void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t & desc );
|
|
|
|
virtual void HandleLateCreation( ) = 0;
|
|
|
|
void Draw( CPrimList *pLists, int nLists );
|
|
|
|
// Copy verts and/or indices to a mesh builder. This only works for temp meshes!
|
|
virtual void CopyToMeshBuilder(
|
|
int iStartVert, // Which vertices to copy.
|
|
int nVerts,
|
|
int iStartIndex, // Which indices to copy.
|
|
int nIndices,
|
|
int indexOffset, // This is added to each index.
|
|
CMeshBuilder &builder );
|
|
|
|
// returns the primitive type
|
|
virtual MaterialPrimitiveType_t GetPrimitiveType() const = 0;
|
|
|
|
// Returns the number of indices in a mesh..
|
|
virtual int IndexCount( ) const = 0;
|
|
|
|
// FIXME: Make this work!
|
|
virtual MaterialIndexFormat_t IndexFormat() const { return MATERIAL_INDEX_FORMAT_16BIT; }
|
|
|
|
// NOTE: For dynamic index buffers only!
|
|
// Casts the memory of the dynamic index buffer to the appropriate type
|
|
virtual void BeginCastBuffer( MaterialIndexFormat_t format ) { Assert(0); }
|
|
virtual void BeginCastBuffer( VertexFormat_t format ) { Assert(0); }
|
|
virtual void EndCastBuffer( ) { Assert(0); }
|
|
virtual int GetRoomRemaining() const { Assert(0); return 0; }
|
|
|
|
// returns a static vertex buffer...
|
|
virtual CVertexBuffer *GetVertexBuffer() { return 0; }
|
|
virtual CIndexBuffer *GetIndexBuffer() { return 0; }
|
|
|
|
// Do I need to reset the vertex format?
|
|
virtual bool NeedsVertexFormatReset( VertexFormat_t fmt ) const;
|
|
|
|
// Do I have enough room?
|
|
virtual bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const;
|
|
|
|
// Operation to do pre-lock
|
|
virtual void PreLock() {}
|
|
|
|
virtual unsigned int ComputeMemoryUsed();
|
|
|
|
bool m_bMeshLocked;
|
|
|
|
protected:
|
|
bool DebugTrace() const;
|
|
|
|
// The vertex format we're using...
|
|
VertexFormat_t m_VertexFormat;
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
IMaterialInternal* m_pMaterial;
|
|
bool m_IsDrawing;
|
|
#endif
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Implementation of the mesh
|
|
//-----------------------------------------------------------------------------
|
|
class CMeshDX8 : public CBaseMeshDX8
|
|
{
|
|
public:
|
|
// constructor
|
|
CMeshDX8( const char *pTextureGroupName );
|
|
virtual ~CMeshDX8();
|
|
|
|
// Locks/unlocks the mesh
|
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc, MeshBuffersAllocationSettings_t *pSettings = 0 );
|
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );
|
|
|
|
// Locks mesh for modifying
|
|
void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
|
|
void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
|
|
void ModifyEnd( MeshDesc_t& desc );
|
|
|
|
// returns the # of vertices (static meshes only)
|
|
int VertexCount() const;
|
|
|
|
// returns the # of indices
|
|
virtual int IndexCount( ) const;
|
|
|
|
// Sets up the vertex and index buffers
|
|
void UseIndexBuffer( CIndexBuffer* pBuffer );
|
|
void UseVertexBuffer( CVertexBuffer* pBuffer );
|
|
|
|
// returns a static vertex buffer...
|
|
CVertexBuffer *GetVertexBuffer() { return m_pVertexBuffer; }
|
|
CIndexBuffer *GetIndexBuffer() { return m_pIndexBuffer; }
|
|
|
|
void SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes );
|
|
virtual void GetColorMesh( const IVertexBuffer** pMesh, int *pMeshVertexOffsetInBytes ) const;
|
|
void SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes );
|
|
void DisableFlexMesh();
|
|
|
|
virtual void HandleLateCreation( );
|
|
|
|
bool HasColorMesh( ) const;
|
|
bool HasFlexMesh( ) const;
|
|
|
|
VertexStreamSpec_t *GetVertexStreamSpec() const;
|
|
void SetVertexStreamSpec( VertexStreamSpec_t *pStreamSpec );
|
|
|
|
virtual void * AccessRawHardwareDataStream( uint8 nRawStreamIndex, uint32 numBytes, uint32 uiFlags, void *pvContext );
|
|
|
|
// Draws the mesh
|
|
void Draw( int nFirstIndex, int nIndexCount );
|
|
virtual void DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex, int nIndexCount );
|
|
void Draw( CPrimList *pLists, int nLists );
|
|
void DrawInternal( const Vector4D *pDiffuseModulation, CPrimList *pLists, int nLists );
|
|
|
|
void DrawPrims( const unsigned char *pInstanceCommandBuffer );
|
|
|
|
// Draws a single pass
|
|
void RenderPass( const unsigned char *pInstanceCommandBuffer );
|
|
void RenderPassForInstances( const unsigned char *pInstanceCommandBuffer );
|
|
|
|
// Sets the primitive type
|
|
void SetPrimitiveType( MaterialPrimitiveType_t type );
|
|
MaterialPrimitiveType_t GetPrimitiveType() const;
|
|
|
|
// Is it using tessellation
|
|
#if ENABLE_TESSELLATION
|
|
TessellationMode_t GetTessellationType() const;
|
|
#else
|
|
TessellationMode_t GetTessellationType() const { return TESSELLATION_MODE_DISABLED; }
|
|
#endif
|
|
|
|
// Is it using vertexID for morphs or subdivision surfaces?
|
|
bool IsUsingVertexID() const;
|
|
|
|
bool IsDynamic() const { return false; }
|
|
|
|
protected:
|
|
// Sets the render state.
|
|
bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, int nIDOffsetBytes = 0, VertexFormat_t vertexFormat = VERTEX_FORMAT_INVALID );
|
|
|
|
// Locks/ unlocks the vertex buffer
|
|
bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc );
|
|
void Unlock( int nVertexCount, VertexDesc_t &desc );
|
|
|
|
// Locks/unlocks the index buffer
|
|
// Pass in nFirstIndex=-1 to lock wherever the index buffer is. Pass in a value
|
|
// >= 0 to specify where to lock.
|
|
int Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &pIndices, MeshBuffersAllocationSettings_t *pSettings = 0 );
|
|
void Unlock( int nIndexCount, IndexDesc_t &desc );
|
|
|
|
// computes how many primitives we've got
|
|
int NumPrimitives( int nVertexCount, int nIndexCount ) const;
|
|
int NumPrimitives( MaterialPrimitiveType_t type, int nIndexCount ) const;
|
|
|
|
// Debugging output...
|
|
void SpewMaterialVerts( );
|
|
|
|
// Stream source setting methods
|
|
void SetVertexIDStreamState( int nIDOffsetBytes );
|
|
void SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel );
|
|
void SetColorStreamState_Internal( CVertexBuffer *pColorVB, int nVertOffset );
|
|
inline void SetColorStreamState( );
|
|
void SetCustomStreamsState( );
|
|
void SetVertexStreamState( int nVertOffsetInBytes, bool bIsRenderingInstances );
|
|
void SetIndexStreamState( int firstVertexIdx );
|
|
|
|
void CheckIndices( CPrimList *pPrim, int numPrimitives );
|
|
void CheckIndices( int nFirstIndex, int numPrimitives );
|
|
|
|
// The vertex and index buffers
|
|
CVertexBuffer* m_pVertexBuffer;
|
|
CIndexBuffer* m_pIndexBuffer;
|
|
|
|
// The current color mesh (to be bound to stream 1)
|
|
// The vertex offset allows use of a global, shared color mesh VB
|
|
CMeshDX8 * m_pColorMesh;
|
|
int m_nColorMeshVertOffsetInBytes;
|
|
|
|
VertexFormat_t m_fmtStreamSpec;
|
|
CArrayAutoPtr< VertexStreamSpec_t > m_pVertexStreamSpec;
|
|
CVertexBuffer *m_pVbTexCoord1;
|
|
LPDIRECT3DVERTEXBUFFER m_arrRawHardwareDataStreams[1];
|
|
|
|
CVertexBuffer *m_pFlexVertexBuffer;
|
|
|
|
bool m_bHasRawHardwareDataStreams;
|
|
bool m_bHasFlexVerts;
|
|
int m_nFlexVertOffsetInBytes;
|
|
int m_flexVertCount;
|
|
|
|
// Primitive type
|
|
MaterialPrimitiveType_t m_Type;
|
|
|
|
// Primitive mode
|
|
D3DPRIMITIVETYPE m_Mode;
|
|
|
|
// Number of primitives
|
|
unsigned int m_NumIndices;
|
|
unsigned short m_NumVertices;
|
|
|
|
// Is it locked?
|
|
bool m_IsVBLocked;
|
|
bool m_IsIBLocked;
|
|
|
|
// Used in rendering sub-parts of the mesh
|
|
static CPrimList *s_pPrims;
|
|
static int s_nPrims;
|
|
static unsigned int s_FirstVertex; // Gets reset during CMeshDX8::DrawInternal
|
|
static unsigned int s_NumVertices;
|
|
int m_FirstIndex;
|
|
|
|
#ifdef RECORDING
|
|
int m_LockVertexBufferSize;
|
|
void *m_LockVertexBuffer;
|
|
#endif
|
|
|
|
#if defined( RECORDING ) || defined( CHECK_INDICES )
|
|
void *m_LockIndexBuffer;
|
|
int m_LockIndexBufferSize;
|
|
#endif
|
|
const char *m_pTextureGroupName;
|
|
|
|
friend class CMeshMgr; // MESHFIXME
|
|
friend void CheckIndices( D3DPRIMITIVETYPE nMode, int nFirstVertex, int nVertexCount, int nBaseIndex, int nFirstIndex, int numPrimitives );
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A little extra stuff for the dynamic version
|
|
//-----------------------------------------------------------------------------
|
|
class CDynamicMeshDX8 : public CMeshDX8
|
|
{
|
|
public:
|
|
// constructor, destructor
|
|
CDynamicMeshDX8();
|
|
virtual ~CDynamicMeshDX8();
|
|
|
|
// Initializes the dynamic mesh
|
|
void Init( int nBufferId );
|
|
|
|
// Sets the vertex format
|
|
virtual void SetVertexFormat( VertexFormat_t format, bool bHasVertexOverride, bool bHasIndexOverride );
|
|
|
|
// Resets the state in case of a task switch
|
|
void Reset();
|
|
|
|
// Do I have enough room in the buffer?
|
|
bool HasEnoughRoom( int nVertexCount, int nIndexCount ) const;
|
|
|
|
// returns the # of indices
|
|
int IndexCount( ) const;
|
|
|
|
// Locks the mesh
|
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc, MeshBuffersAllocationSettings_t *pSettings = 0 );
|
|
|
|
// Unlocks the mesh
|
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc );
|
|
|
|
// Override vertex + index buffer
|
|
void OverrideVertexBuffer( CVertexBuffer *pStaticVertexBuffer );
|
|
void OverrideIndexBuffer( CIndexBuffer *pStaticIndexBuffer );
|
|
|
|
// Do I need to reset the vertex format?
|
|
bool NeedsVertexFormatReset(VertexFormat_t fmt) const;
|
|
|
|
// Draws it
|
|
void Draw( int nFirstIndex, int nIndexCount );
|
|
virtual void DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex, int nIndexCount );
|
|
void MarkAsDrawn() { m_HasDrawn = true; }
|
|
// Simply draws what's been buffered up immediately, without state change
|
|
void DrawSinglePassImmediately();
|
|
|
|
// Operation to do pre-lock
|
|
void PreLock();
|
|
|
|
bool IsDynamic() const { return true; }
|
|
|
|
private:
|
|
// Resets buffering state
|
|
void ResetVertexAndIndexCounts();
|
|
|
|
void DrawInternal( const Vector4D *pVecDiffuseModulation, int nFirstIndex, int nIndexCount );
|
|
|
|
// Buffer Id
|
|
int m_nBufferId;
|
|
|
|
// total queued vertices
|
|
int m_TotalVertices;
|
|
int m_TotalIndices;
|
|
|
|
// the first vertex and index since the last draw
|
|
int m_nFirstVertex;
|
|
int m_FirstIndex;
|
|
|
|
// Have we drawn since the last lock?
|
|
bool m_HasDrawn;
|
|
|
|
// Any overrides?
|
|
bool m_VertexOverride;
|
|
bool m_IndexOverride;
|
|
};
|
|
|
|
|
|
#ifdef _GAMECONSOLE
|
|
//-----------------------------------------------------------------------------
|
|
// For use as a mesh that we've already written into write-combined memory
|
|
//-----------------------------------------------------------------------------
|
|
class CExternalMeshDX8 : public CMeshDX8
|
|
{
|
|
typedef CMeshDX8 BaseClass;
|
|
|
|
public:
|
|
// constructor, destructor
|
|
CExternalMeshDX8() : BaseClass( "external vb - ignore" )
|
|
{
|
|
m_pVertexBufferExternal = new CVertexBuffer;
|
|
m_pIndexBufferExternal = new CIndexBuffer;
|
|
}
|
|
|
|
virtual ~CExternalMeshDX8()
|
|
{
|
|
CleanUp();
|
|
}
|
|
|
|
void CleanUp()
|
|
{
|
|
if ( m_pVertexBufferExternal )
|
|
{
|
|
if( m_pVertexBufferExternal == m_pVertexBuffer )
|
|
{
|
|
m_pVertexBuffer = NULL; // let's avoid double-delete
|
|
}
|
|
delete m_pVertexBufferExternal;
|
|
m_pVertexBufferExternal = NULL;
|
|
}
|
|
|
|
if ( m_pIndexBufferExternal )
|
|
{
|
|
if( m_pIndexBufferExternal == m_pIndexBuffer )
|
|
{
|
|
m_pIndexBuffer = NULL; // let's avoid double-delete
|
|
}
|
|
delete m_pIndexBufferExternal;
|
|
m_pIndexBufferExternal = NULL;
|
|
}
|
|
}
|
|
|
|
// Initializes the mesh
|
|
void Init( const ExternalMeshInfo_t& info )
|
|
{
|
|
m_NumVertices = 0;
|
|
m_NumIndices = 0;
|
|
|
|
// SetMaterial is only for debugging;
|
|
// it actually shows up a tiny bit on the profile might as well ifdef it
|
|
#ifdef _DEBUG
|
|
SetMaterial( info.m_pMaterial );
|
|
#endif
|
|
if ( info.m_pVertexOverride )
|
|
{
|
|
CBaseMeshDX8 *pDX8Mesh = static_cast<CBaseMeshDX8*>( info.m_pVertexOverride );
|
|
SetVertexFormat( pDX8Mesh->GetVertexFormat(), true, ( info.m_pIndexOverride != NULL ) );
|
|
m_pVertexBuffer = pDX8Mesh->GetVertexBuffer();
|
|
}
|
|
else
|
|
{
|
|
SetVertexFormat( info.m_VertexFormat, false, ( info.m_pIndexOverride != NULL ) );
|
|
m_pVertexBuffer = m_pVertexBufferExternal;
|
|
}
|
|
|
|
if ( info.m_pIndexOverride )
|
|
{
|
|
CBaseMeshDX8 *pDX8Mesh = static_cast<CBaseMeshDX8*>( info.m_pIndexOverride );
|
|
m_pIndexBuffer = pDX8Mesh->GetIndexBuffer();
|
|
}
|
|
else
|
|
{
|
|
m_pIndexBuffer = m_pIndexBufferExternal;
|
|
}
|
|
}
|
|
|
|
void SetExternalData( const ExternalMeshData_t &data )
|
|
{
|
|
if ( m_pVertexBuffer == m_pVertexBufferExternal )
|
|
{
|
|
if ( data.m_nVertexCount > 0 )
|
|
{
|
|
m_pVertexBuffer->Init( Dx9Device(), GetVertexFormat(),
|
|
0, data.m_pVertexData, data.m_nVertexSizeInBytes, data.m_nVertexCount );
|
|
m_NumVertices = data.m_nVertexCount;
|
|
}
|
|
else
|
|
{
|
|
m_pVertexBuffer = NULL;
|
|
m_NumVertices = 0;
|
|
}
|
|
}
|
|
|
|
if ( m_pIndexBuffer == m_pIndexBufferExternal )
|
|
{
|
|
if ( data.m_nIndexCount > 0 )
|
|
{
|
|
m_pIndexBuffer->Init( Dx9Device(), data.m_pIndexData, data.m_nIndexCount );
|
|
m_NumIndices = data.m_nIndexCount;
|
|
}
|
|
else
|
|
{
|
|
m_pIndexBuffer = NULL;
|
|
m_NumIndices = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual bool IsExternal() const { return true; }
|
|
|
|
private:
|
|
CVertexBuffer *m_pVertexBufferExternal;
|
|
CIndexBuffer *m_pIndexBufferExternal;
|
|
};
|
|
#endif // _GAMECONSOLE
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A mesh that stores temporary vertex data in the correct format (for modification)
|
|
//-----------------------------------------------------------------------------
|
|
class CTempMeshDX8 : public CBaseMeshDX8
|
|
{
|
|
public:
|
|
// constructor, destructor
|
|
CTempMeshDX8( bool isDynamic );
|
|
virtual ~CTempMeshDX8();
|
|
|
|
// Sets the material
|
|
virtual void SetVertexFormat( VertexFormat_t format, bool bHasVertexOverride, bool bHasIndexOverride );
|
|
|
|
// Locks/unlocks the mesh
|
|
void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc, MeshBuffersAllocationSettings_t *pSettings = 0 );
|
|
void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc );
|
|
|
|
// Locks mesh for modifying
|
|
virtual void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
|
|
virtual void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc );
|
|
virtual void ModifyEnd( MeshDesc_t& desc );
|
|
|
|
// Number of indices + vertices
|
|
int VertexCount() const;
|
|
virtual int IndexCount() const;
|
|
virtual bool IsDynamic() const;
|
|
|
|
// Sets the primitive type
|
|
void SetPrimitiveType( MaterialPrimitiveType_t type );
|
|
MaterialPrimitiveType_t GetPrimitiveType() const;
|
|
|
|
// Using tessellation for subd surfaces?
|
|
TessellationMode_t GetTessellationType() const { return TESSELLATION_MODE_DISABLED; }
|
|
|
|
// Begins a pass
|
|
void BeginPass( );
|
|
|
|
virtual void DrawPrims( const unsigned char *pInstanceCommandBuffer ) {}
|
|
|
|
// Draws a single pass
|
|
void RenderPass( const unsigned char *pInstanceCommandBuffer );
|
|
|
|
virtual void HandleLateCreation()
|
|
{
|
|
Assert( !"TBD - CTempMeshDX8::HandleLateCreation()" );
|
|
}
|
|
|
|
// Draws the entire beast
|
|
void Draw( int nFirstIndex, int nIndexCount );
|
|
virtual void DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex, int nIndexCount );
|
|
|
|
virtual void CopyToMeshBuilder(
|
|
int iStartVert, // Which vertices to copy.
|
|
int nVerts,
|
|
int iStartIndex, // Which indices to copy.
|
|
int nIndices,
|
|
int indexOffset, // This is added to each index.
|
|
CMeshBuilder &builder );
|
|
private:
|
|
// Selection mode
|
|
void TestSelection( );
|
|
void ClipTriangle( D3DXVECTOR3 **ppVert, float zNear, D3DXMATRIX &proj );
|
|
void DrawInternal( const Vector4D *pVecDiffuseModulation, int nFirstIndex, int nIndexCount );
|
|
|
|
CDynamicMeshDX8 *GetDynamicMesh();
|
|
|
|
CUtlVector< unsigned char, CUtlMemoryAligned< unsigned char, 32 > > m_VertexData;
|
|
CUtlVector< unsigned short > m_IndexData;
|
|
|
|
unsigned short m_VertexSize;
|
|
MaterialPrimitiveType_t m_Type;
|
|
int m_LockedVerts;
|
|
int m_LockedIndices;
|
|
bool m_IsDynamic;
|
|
|
|
// Used in rendering sub-parts of the mesh
|
|
static unsigned int s_NumIndices;
|
|
static unsigned int s_FirstIndex;
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
bool m_Locked;
|
|
bool m_InPass;
|
|
#endif
|
|
};
|
|
|
|
#if 0
|
|
//-----------------------------------------------------------------------------
|
|
// A mesh that stores temporary vertex data in the correct format (for modification)
|
|
//-----------------------------------------------------------------------------
|
|
class CTempIndexBufferDX8 : public CIndexBufferBase
|
|
{
|
|
public:
|
|
// constructor, destructor
|
|
CTempIndexBufferDX8( bool isDynamic );
|
|
virtual ~CTempIndexBufferDX8();
|
|
|
|
// Locks/unlocks the mesh
|
|
void LockIndexBuffer( int nIndexCount );
|
|
void UnlockMesh( int nIndexCount );
|
|
|
|
// Locks mesh for modifying
|
|
virtual void ModifyBeginEx( bool bReadOnly, int nFirstIndex, int nIndexCount );
|
|
virtual void ModifyEnd();
|
|
|
|
// Number of indices
|
|
virtual int IndexCount() const;
|
|
virtual bool IsDynamic() const;
|
|
|
|
virtual void CopyToIndexBuilder(
|
|
int iStartIndex, // Which indices to copy.
|
|
int nIndices,
|
|
int indexOffset, // This is added to each index.
|
|
CIndexBuilder &builder );
|
|
private:
|
|
// Selection mode
|
|
void TestSelection( );
|
|
|
|
CDynamicMeshDX8 *GetDynamicMesh();
|
|
|
|
CUtlVector< unsigned short > m_IndexData;
|
|
|
|
MaterialPrimitiveType_t m_Type;
|
|
int m_LockedIndices;
|
|
bool m_IsDynamic;
|
|
|
|
// Used in rendering sub-parts of the mesh
|
|
static unsigned int s_NumIndices;
|
|
static unsigned int s_FirstIndex;
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
bool m_Locked;
|
|
bool m_InPass;
|
|
#endif
|
|
};
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Implementation of the mesh manager
|
|
//-----------------------------------------------------------------------------
|
|
class CMeshMgr : public IMeshMgr
|
|
{
|
|
public:
|
|
// constructor, destructor
|
|
CMeshMgr();
|
|
virtual ~CMeshMgr();
|
|
|
|
// Initialize, shutdown
|
|
void Init();
|
|
void Shutdown();
|
|
|
|
// Task switch...
|
|
void ReleaseBuffers();
|
|
void RestoreBuffers();
|
|
|
|
// Releases all dynamic vertex buffers
|
|
void DestroyVertexBuffers();
|
|
|
|
// Flushes the vertex buffers
|
|
void DiscardVertexBuffers();
|
|
|
|
// Creates, destroys static meshes
|
|
IMesh *CreateStaticMesh( VertexFormat_t vertexFormat, const char *pTextureBudgetGroup, IMaterial *pMaterial = NULL, VertexStreamSpec_t *pStreamSpec = NULL );
|
|
void DestroyStaticMesh( IMesh *pMesh );
|
|
|
|
// Gets at the dynamic mesh (spoofs it though)
|
|
IMesh *GetDynamicMesh( IMaterial *pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount, bool buffered,
|
|
IMesh *pVertexOverride, IMesh *pIndexOverride );
|
|
|
|
// -----------------------------------------------------------
|
|
// ------------ New Vertex/Index Buffer interface ----------------------------
|
|
// Do we need support for bForceTempMesh and bSoftwareVertexShader?
|
|
// I don't think we use bSoftwareVertexShader anymore. .need to look into bForceTempMesh.
|
|
IVertexBuffer *CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup );
|
|
IIndexBuffer *CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup );
|
|
void DestroyVertexBuffer( IVertexBuffer * );
|
|
void DestroyIndexBuffer( IIndexBuffer * );
|
|
// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams?
|
|
IVertexBuffer *GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered = true );
|
|
IIndexBuffer *GetDynamicIndexBuffer( );
|
|
void BindVertexBuffer( int streamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions = 1 );
|
|
void BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes );
|
|
void Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount );
|
|
// ------------ End ----------------------------
|
|
void RenderPassWithVertexAndIndexBuffers( const unsigned char *pInstanceCommandBuffer );
|
|
|
|
VertexFormat_t GetCurrentVertexFormat( void ) const { return m_CurrentVertexFormat; }
|
|
|
|
// Gets at the *actual* dynamic mesh
|
|
IMesh* GetActualDynamicMesh( VertexFormat_t vertexFormat );
|
|
IMesh *GetFlexMesh();
|
|
|
|
// Computes vertex format from a list of ingredients
|
|
VertexFormat_t ComputeVertexFormat( unsigned int flags,
|
|
int numTexCoords, int *pTexCoordDimensions, int numBoneWeights,
|
|
int userDataSize ) const;
|
|
|
|
// Use fat vertices (for tools)
|
|
virtual void UseFatVertices( bool bUseFat );
|
|
|
|
// Returns the number of vertices we can render using the dynamic mesh
|
|
virtual void GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices );
|
|
virtual int GetMaxVerticesToRender( IMaterial *pMaterial );
|
|
virtual int GetMaxIndicesToRender( );
|
|
|
|
// Returns a vertex buffer appropriate for the flags
|
|
CVertexBuffer *FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t fmt );
|
|
CIndexBuffer *GetDynamicIndexBufferInternal();
|
|
|
|
// Is the mesh dynamic?
|
|
bool IsDynamicMesh( IMesh *pMesh ) const;
|
|
|
|
// Is the vertex buffer dynamic?
|
|
bool IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const;
|
|
|
|
// Is the index buffer dynamic?
|
|
bool IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const;
|
|
|
|
// Returns the vertex size
|
|
int VertexFormatSize( VertexFormat_t vertexFormat ) const
|
|
{
|
|
return CVertexBufferBase::VertexFormatSize( vertexFormat );
|
|
}
|
|
|
|
// Computes the vertex buffer pointers
|
|
void ComputeVertexDescription( unsigned char *pBuffer,
|
|
VertexFormat_t vertexFormat, MeshDesc_t &desc ) const;
|
|
|
|
// Returns the number of buffers...
|
|
int BufferCount() const
|
|
{
|
|
#ifdef _DEBUG
|
|
return CVertexBuffer::BufferCount() + CIndexBuffer::BufferCount();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
CVertexBuffer *GetVertexIDBuffer();
|
|
CVertexBuffer *GetEmptyColorBuffer();
|
|
|
|
CIndexBuffer *GetPreTessPatchIndexBuffer( int iSubdivLevel );
|
|
CVertexBuffer *GetPreTessPatchVertexBuffer( int iSubdivLevel );
|
|
|
|
// Helper to determine the number of indices for a particular patch subdiv level
|
|
static int GetNumIndicesForSubdivisionLevel( int iSubdivLevel );
|
|
|
|
IVertexBuffer *GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered = true );
|
|
virtual void MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords );
|
|
virtual void DrawInstances( int nInstanceCount, const MeshInstanceData_t *pInstances );
|
|
|
|
#ifdef _GAMECONSOLE
|
|
virtual int GetDynamicIndexBufferAllocationCount();
|
|
virtual int GetDynamicIndexBufferIndicesLeft();
|
|
|
|
// Backdoor used by the queued context to directly use write-combined memory
|
|
virtual IMesh *GetExternalMesh( const ExternalMeshInfo_t& info );
|
|
virtual void SetExternalMeshData( IMesh *pMesh, const ExternalMeshData_t &data );
|
|
virtual IIndexBuffer *GetExternalIndexBuffer( int nIndexCount, uint16 *pIndexData );
|
|
#endif
|
|
|
|
int UnusedVertexFields() const { return m_nUnusedVertexFields; }
|
|
int UnusedTextureCoords() const { return m_nUnusedTextureCoords; }
|
|
|
|
void RenderPassForInstances( const unsigned char *pInstanceCommandBuffer );
|
|
void DrawInstancedPrims( const unsigned char *pInstanceCommandBuffer );
|
|
|
|
IDirect3DVertexBuffer9 *GetZeroVertexBuffer() const { return m_pZeroVertexBuffer; }
|
|
|
|
// Mesh builder used to modify vertex data
|
|
CMeshBuilder m_ModifyBuilder;
|
|
|
|
private:
|
|
void SetVertexIDStreamState( int nIDOffsetBytes );
|
|
void SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel );
|
|
void SetColorStreamState( );
|
|
void SetCustomStreamsState( );
|
|
void SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride );
|
|
void SetIndexStreamState( int firstVertexIdx );
|
|
bool SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride );
|
|
|
|
struct VertexBufferLookup_t
|
|
{
|
|
CVertexBuffer* m_pBuffer;
|
|
int m_VertexSize;
|
|
};
|
|
|
|
void CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh, CMeshDX8 *pSrcIndexMesh );
|
|
|
|
// Cleans up the class
|
|
void CleanUp();
|
|
|
|
// Creates, destroys the vertexID buffer
|
|
void CreateVertexIDBuffer();
|
|
void DestroyVertexIDBuffer();
|
|
|
|
// Creates, destroys the empty color buffer
|
|
void CreateEmptyColorBuffer();
|
|
void DestroyEmptyColorBuffer();
|
|
|
|
void CreateZeroVertexBuffer();
|
|
void DestroyZeroVertexBuffer();
|
|
|
|
// Fills a vertexID buffer
|
|
void FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount );
|
|
void FillEmptyColorBuffer( CVertexBuffer *pEmptyColorBuffer, int nCount );
|
|
|
|
// struct for our patch vertices
|
|
struct PreTessPatchVertex_t
|
|
{
|
|
Vector2D m_vPatchUV;
|
|
Vector4D m_vBasisU;
|
|
Vector4D m_vBasisV;
|
|
};
|
|
|
|
// Creates, destroys the pre-tessellated patch buffers
|
|
void CreatePreTessPatchIndexBuffers();
|
|
void CreatePreTessPatchVertexBuffers();
|
|
void DestroyPreTessPatchIndexBuffers();
|
|
void DestroyPreTessPatchVertexBuffers();
|
|
|
|
// Fills a pre-tessellated patch IB / VB
|
|
void FillPreTessPatchIB( CIndexBuffer* pIndexBuffer, int iSubdivLevel, int nIndexCount );
|
|
void FillPreTessPatchVB( CVertexBuffer* pVertexBuffer, int iSubdivLevel, int nVertexCount );
|
|
|
|
// The dynamic index buffer
|
|
CIndexBuffer *m_pDynamicIndexBuffer;
|
|
|
|
// A static vertexID buffer
|
|
CVertexBuffer *m_pVertexIDBuffer;
|
|
|
|
// Used when we don't have a static color buffer
|
|
CVertexBuffer *m_pEmptyColorBuffer;
|
|
|
|
// Static pre-tessellated patch index/vertex buffers.
|
|
CIndexBuffer *m_pPreTessPatchIndexBuffer[ MAX_TESS_DIVISIONS_PER_SIDE ];
|
|
CVertexBuffer *m_pPreTessPatchVertexBuffer[ MAX_TESS_DIVISIONS_PER_SIDE ];
|
|
|
|
// The dynamic vertex buffers
|
|
CUtlVector< VertexBufferLookup_t > m_DynamicVertexBuffers;
|
|
|
|
// The current dynamic mesh
|
|
CDynamicMeshDX8 m_DynamicMesh;
|
|
CDynamicMeshDX8 m_DynamicFlexMesh;
|
|
|
|
// The current dynamic vertex buffer
|
|
CVertexBufferDx8 m_DynamicVertexBuffer;
|
|
|
|
// The current dynamic index buffer
|
|
CIndexBufferDx8 m_DynamicIndexBuffer;
|
|
|
|
// The dynamic mesh temp version (for shaders that modify vertex data)
|
|
CTempMeshDX8 m_DynamicTempMesh;
|
|
|
|
// Using fat vertices?
|
|
bool m_bUseFatVertices;
|
|
|
|
CVertexBufferDx8 *m_pCurrentVertexBuffer;
|
|
VertexFormat_t m_CurrentVertexFormat;
|
|
int m_pVertexBufferOffset[MAX_DX8_STREAMS];
|
|
int m_pCurrentVertexStride[MAX_DX8_STREAMS];
|
|
int m_pFirstVertex[MAX_DX8_STREAMS];
|
|
int m_pVertexCount[MAX_DX8_STREAMS];
|
|
CIndexBufferBase *m_pCurrentIndexBuffer;
|
|
int m_nIndexBufferOffset;
|
|
MaterialPrimitiveType_t m_PrimitiveType;
|
|
int m_nFirstIndex;
|
|
int m_nNumIndices;
|
|
|
|
unsigned int m_nUnusedVertexFields;
|
|
unsigned int m_nUnusedTextureCoords;
|
|
|
|
// 4096 byte static VB containing all-zeros
|
|
IDirect3DVertexBuffer9 *m_pZeroVertexBuffer;
|
|
|
|
#ifdef _GAMECONSOLE
|
|
CExternalMeshDX8 m_ExternalMesh;
|
|
CExternalMeshDX8 m_ExternalFlexMesh;
|
|
CExternalIndexBufferDx8 m_ExternalIndexBuffer;
|
|
#endif // _GAMECONSOLE
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton...
|
|
//-----------------------------------------------------------------------------
|
|
static CMeshMgr g_MeshMgr;
|
|
IMeshMgr* MeshMgr()
|
|
{
|
|
return &g_MeshMgr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tracks stream state and queued data
|
|
//-----------------------------------------------------------------------------
|
|
static CIndexBuffer *g_pLastIndex = NULL;
|
|
static IDirect3DIndexBuffer9 *g_pLastIndexBuffer = NULL;
|
|
static CVertexBuffer *g_pLastVertex = NULL;
|
|
static IDirect3DVertexBuffer9 *g_pLastVertexBuffer = NULL;
|
|
static int g_nLastVertOffsetInBytes = 0;
|
|
static int g_nLastVertStride = 0;
|
|
static int g_LastVertexIdx = -1;
|
|
static CVertexBuffer *g_pLastColorBuffer = NULL;
|
|
static VertexStreamSpec_t *g_pLastStreamSpec = NULL;
|
|
static void *g_pLastRawHardwareDataStream = NULL;
|
|
static bool g_bCustomStreamsSet[ 16 ];
|
|
static int g_nLastColorMeshVertOffsetInBytes = 0;
|
|
static bool g_bUsingVertexID = false;
|
|
static int g_nLastVertexIDOffset = -1;
|
|
static bool g_bUsingPreTessPatches = false;
|
|
static VertexFormat_t g_LastVertexFormat = 0;
|
|
|
|
inline void D3DSetStreamSource( unsigned int streamNumber, IDirect3DVertexBuffer9 *pStreamData,
|
|
unsigned int nVertexOffsetInBytes, unsigned int stride )
|
|
{
|
|
Dx9Device()->SetStreamSource( streamNumber, pStreamData, nVertexOffsetInBytes, stride );
|
|
}
|
|
|
|
inline void D3DSetIndices( IDirect3DIndexBuffer9 *pIndexBuffer )
|
|
{
|
|
if ( g_pLastIndexBuffer != pIndexBuffer )
|
|
{
|
|
Dx9Device()->SetIndices( pIndexBuffer );
|
|
g_pLastIndexBuffer = pIndexBuffer;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tracks stream state and queued data
|
|
//-----------------------------------------------------------------------------
|
|
void Unbind( IDirect3DIndexBuffer9 *pIndexBuffer )
|
|
{
|
|
#ifdef _X360
|
|
IDirect3DIndexBuffer9 *pBoundBuffer;
|
|
Dx9Device()->GetIndices( &pBoundBuffer );
|
|
if ( pBoundBuffer == pIndexBuffer )
|
|
{
|
|
// xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices
|
|
Dx9Device()->SetIndices( NULL );
|
|
g_pLastIndex = NULL;
|
|
g_pLastIndexBuffer = NULL;
|
|
}
|
|
|
|
if ( pBoundBuffer )
|
|
{
|
|
pBoundBuffer->Release();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Unbind( IDirect3DVertexBuffer9 *pVertexBuffer )
|
|
{
|
|
#ifdef _X360
|
|
UINT nOffset, nStride;
|
|
IDirect3DVertexBuffer9 *pBoundBuffer;
|
|
for ( int i = 0; i < MAX_DX8_STREAMS; ++i )
|
|
{
|
|
Dx9Device()->GetStreamSource( i, &pBoundBuffer, &nOffset, &nStride );
|
|
if ( pBoundBuffer == pVertexBuffer )
|
|
{
|
|
// xboxissue - cannot lock indexes set in a d3d device, clear possibly set indices
|
|
Dx9Device()->SetStreamSource( i, 0, 0, 0 );
|
|
switch ( i )
|
|
{
|
|
case 0:
|
|
g_pLastVertex = NULL;
|
|
g_pLastVertexBuffer = NULL;
|
|
break;
|
|
|
|
case 1:
|
|
g_pLastColorBuffer = NULL;
|
|
g_nLastColorMeshVertOffsetInBytes = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( pBoundBuffer )
|
|
{
|
|
pBoundBuffer->Release();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helpers to count texture coordinates
|
|
//-----------------------------------------------------------------------------
|
|
static int NumTextureCoordinates( VertexFormat_t vertexFormat )
|
|
{
|
|
int nTexCoordCount = 0;
|
|
for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i )
|
|
{
|
|
if ( TexCoordSize( i, vertexFormat ) == 0 )
|
|
continue;
|
|
++nTexCoordCount;
|
|
}
|
|
return nTexCoordCount;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Makes sure that the render state is always set next time
|
|
//-----------------------------------------------------------------------------
|
|
static void ResetMeshRenderState()
|
|
{
|
|
g_pLastIndex = 0;
|
|
g_pLastIndexBuffer = 0;
|
|
g_pLastVertex = 0;
|
|
g_nLastVertOffsetInBytes = 0;
|
|
g_pLastColorBuffer = 0;
|
|
g_nLastColorMeshVertOffsetInBytes = 0;
|
|
g_LastVertexIdx = -1;
|
|
g_bUsingVertexID = false;
|
|
g_nLastVertexIDOffset = -1;
|
|
g_bUsingPreTessPatches = false;
|
|
g_LastVertexFormat = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Makes sure that the render state is always set next time
|
|
//-----------------------------------------------------------------------------
|
|
static void ResetIndexBufferRenderState()
|
|
{
|
|
g_pLastIndex = 0;
|
|
g_pLastIndexBuffer = 0;
|
|
g_LastVertexIdx = -1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Index Buffer implementations begin here
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Globals
|
|
//-----------------------------------------------------------------------------
|
|
#ifdef _DEBUG
|
|
int CIndexBufferDx8::s_nBufferCount = 0;
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CIndexBufferDx8::CIndexBufferDx8( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroupName ) :
|
|
BaseClass( pBudgetGroupName )
|
|
{
|
|
// Debugger();
|
|
|
|
// Assert( nIndexCount != 0 );
|
|
|
|
// NOTE: MATERIAL_INDEX_FORMAT_UNKNOWN can't be dealt with under dx9
|
|
// because format is bound at buffer creation time. What we'll do
|
|
// is just arbitrarily choose to use a 16-bit index buffer of the same size
|
|
if ( fmt == MATERIAL_INDEX_FORMAT_UNKNOWN )
|
|
{
|
|
fmt = MATERIAL_INDEX_FORMAT_16BIT;
|
|
nIndexCount /= 2;
|
|
}
|
|
|
|
m_pIndexBuffer = NULL;
|
|
m_IndexFormat = fmt;
|
|
m_nBufferSize = nIndexCount * IndexSize();
|
|
m_nIndexCount = nIndexCount;
|
|
m_nFirstUnwrittenOffset = 0;
|
|
m_bIsLocked = false;
|
|
m_bIsDynamic = IsDynamicBufferType( bufferType );
|
|
m_bFlush = false;
|
|
|
|
#ifdef CHECK_INDICES
|
|
m_pShadowIndices = NULL;
|
|
#endif
|
|
|
|
#ifdef VPROF_ENABLED
|
|
m_nVProfFrame = -1;
|
|
#endif
|
|
}
|
|
|
|
CIndexBufferDx8::~CIndexBufferDx8()
|
|
{
|
|
Free();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the index size
|
|
//-----------------------------------------------------------------------------
|
|
inline int CIndexBufferDx8::IndexSize() const
|
|
{
|
|
Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN );
|
|
return ( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT ) ? 2 : 4;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates, destroys the index buffer
|
|
//-----------------------------------------------------------------------------
|
|
bool CIndexBufferDx8::Allocate()
|
|
{
|
|
Assert( !m_pIndexBuffer );
|
|
m_nFirstUnwrittenOffset = 0;
|
|
|
|
// FIXME: This doesn't really work for dynamic buffers; dynamic buffers
|
|
// can't have mixed-type indices in them. Bleah.
|
|
D3DFORMAT format = ( m_IndexFormat == MATERIAL_INDEX_FORMAT_32BIT ) ?
|
|
D3DFMT_INDEX32 : D3DFMT_INDEX16;
|
|
|
|
DWORD usage = D3DUSAGE_WRITEONLY;
|
|
if ( m_bIsDynamic )
|
|
{
|
|
usage |= D3DUSAGE_DYNAMIC;
|
|
}
|
|
|
|
D3DPOOL d3dPool = m_bIsDynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
|
|
HRESULT hr = Dx9Device()->CreateIndexBuffer(
|
|
m_nBufferSize, usage, format, d3dPool, &m_pIndexBuffer, NULL );
|
|
|
|
#if !defined( _X360 )
|
|
if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) )
|
|
{
|
|
// Don't have the memory for this. Try flushing all managed resources
|
|
// out of vid mem and try again.
|
|
// FIXME: need to record this
|
|
Dx9Device()->EvictManagedResources();
|
|
hr = Dx9Device()->CreateIndexBuffer(
|
|
m_nBufferSize, usage, format, d3dPool, &m_pIndexBuffer, NULL );
|
|
}
|
|
#endif // !X360
|
|
|
|
if ( FAILED(hr) || ( m_pIndexBuffer == NULL ) )
|
|
{
|
|
Warning( "CIndexBufferDx8::Allocate: CreateIndexBuffer failed!\n" );
|
|
return false;
|
|
}
|
|
|
|
if ( !m_bIsDynamic )
|
|
{
|
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER,
|
|
COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize );
|
|
}
|
|
else
|
|
{
|
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER,
|
|
COUNTER_GROUP_TEXTURE_GLOBAL, m_nBufferSize );
|
|
}
|
|
|
|
#ifdef CHECK_INDICES
|
|
Assert ( !m_pShadowIndices );
|
|
m_pShadowIndices = new unsigned char[ m_nBufferSize ];
|
|
memset( m_pShadowIndices, 0xFF, m_nBufferSize );
|
|
#endif // CHECK_INDICES
|
|
|
|
#ifdef _DEBUG
|
|
++s_nBufferCount;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void CIndexBufferDx8::Free()
|
|
{
|
|
// FIXME: Unlock(0);
|
|
if ( m_pIndexBuffer )
|
|
{
|
|
#ifdef _DEBUG
|
|
--s_nBufferCount;
|
|
#endif
|
|
|
|
#if SHADERAPI_NO_D3DDeviceWrapper
|
|
m_pIndexBuffer->Release();
|
|
#else
|
|
Dx9Device()->Release( m_pIndexBuffer );
|
|
#endif
|
|
m_pIndexBuffer = NULL;
|
|
|
|
if ( !m_bIsDynamic )
|
|
{
|
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER,
|
|
COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize );
|
|
}
|
|
else
|
|
{
|
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER,
|
|
COUNTER_GROUP_TEXTURE_GLOBAL, - m_nBufferSize );
|
|
}
|
|
}
|
|
|
|
#ifdef CHECK_INDICES
|
|
if ( m_pShadowIndices )
|
|
{
|
|
delete[] m_pShadowIndices;
|
|
m_pShadowIndices = NULL;
|
|
}
|
|
#endif // CHECK_INDICES
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Index buffer information
|
|
//-----------------------------------------------------------------------------
|
|
int CIndexBufferDx8::IndexCount( ) const
|
|
{
|
|
Assert( !m_bIsDynamic );
|
|
return m_nIndexCount;
|
|
}
|
|
|
|
MaterialIndexFormat_t CIndexBufferDx8::IndexFormat() const
|
|
{
|
|
Assert( !m_bIsDynamic );
|
|
return m_IndexFormat;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns true if the buffer is dynamic
|
|
//-----------------------------------------------------------------------------
|
|
bool CIndexBufferDx8::IsDynamic() const
|
|
{
|
|
return m_bIsDynamic;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Only used by dynamic buffers, indicates the next lock should perform a discard.
|
|
//-----------------------------------------------------------------------------
|
|
void CIndexBufferDx8::Flush()
|
|
{
|
|
// This strange-looking line makes a flush only occur if the buffer is dynamic.
|
|
m_bFlush = m_bIsDynamic;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the D3D buffer
|
|
//-----------------------------------------------------------------------------
|
|
IDirect3DIndexBuffer9* CIndexBufferDx8::GetDx9Buffer()
|
|
{
|
|
return m_pIndexBuffer;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns a shadowed index, for validation
|
|
//-----------------------------------------------------------------------------
|
|
#ifdef CHECK_INDICES
|
|
unsigned short CIndexBufferDx8::GetShadowIndex( int i ) const
|
|
{
|
|
Assert( i >= 0 && i < m_nIndexCount );
|
|
Assert( m_IndexFormat == MATERIAL_INDEX_FORMAT_16BIT );
|
|
return *(unsigned short*)( &m_pShadowIndices[ i * IndexSize() ] );
|
|
}
|
|
#endif // CHECK_INDICES
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Used to measure how much static buffer memory is touched each frame
|
|
//-----------------------------------------------------------------------------
|
|
void CIndexBufferDx8::HandlePerFrameTextureStats( int nFrame )
|
|
{
|
|
#ifdef VPROF_ENABLED
|
|
if ( m_nVProfFrame != nFrame && !m_bIsDynamic )
|
|
{
|
|
m_nVProfFrame = nFrame;
|
|
VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_STATIC_INDEX_BUFFER,
|
|
COUNTER_GROUP_TEXTURE_PER_FRAME, m_nBufferSize );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Casts a dynamic buffer to be a particular index type
|
|
//-----------------------------------------------------------------------------
|
|
void CIndexBufferDx8::BeginCastBuffer( MaterialIndexFormat_t format )
|
|
{
|
|
// NOTE: This should have no effect under Dx9, since we can't recast index buffers.
|
|
Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN );
|
|
Assert( m_bIsDynamic && ( m_IndexFormat == format ) );
|
|
}
|
|
|
|
void CIndexBufferDx8::EndCastBuffer( )
|
|
{
|
|
// NOTE: This should have no effect under Dx9, since we can't recast index buffers.
|
|
}
|
|
|
|
int CIndexBufferDx8::GetRoomRemaining() const
|
|
{
|
|
return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / IndexSize();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the index buffer
|
|
//-----------------------------------------------------------------------------
|
|
bool CIndexBufferDx8::Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc )
|
|
{
|
|
Assert( !m_bIsLocked && ( nMaxIndexCount != 0 ) && ( nMaxIndexCount <= m_nIndexCount ) );
|
|
Assert( m_IndexFormat != MATERIAL_INDEX_FORMAT_UNKNOWN );
|
|
|
|
// FIXME: Why do we need to sync matrices now?
|
|
ShaderUtil()->SyncMatrices();
|
|
g_ShaderMutex.Lock();
|
|
|
|
VPROF( "CIndexBufferX8::Lock" );
|
|
|
|
void *pLockedData = NULL;
|
|
HRESULT hr;
|
|
int nMemoryRequired;
|
|
bool bHasEnoughMemory;
|
|
UINT nLockFlags;
|
|
|
|
// This can happen if the buffer was locked but a type wasn't bound
|
|
if ( m_IndexFormat == MATERIAL_INDEX_FORMAT_UNKNOWN )
|
|
goto indexBufferLockFailed;
|
|
|
|
// Just give the app crap buffers to fill up while we're suppressed...
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxIndexCount == 0 ) )
|
|
goto indexBufferLockFailed;
|
|
|
|
// Did we ask for something too large?
|
|
if ( nMaxIndexCount > m_nIndexCount )
|
|
{
|
|
Warning( "Too many indices for index buffer. . tell a programmer (%d>%d)\n", nMaxIndexCount, m_nIndexCount );
|
|
goto indexBufferLockFailed;
|
|
}
|
|
|
|
// We might not have a buffer owing to alt-tab type stuff
|
|
if ( !m_pIndexBuffer )
|
|
{
|
|
if ( !Allocate() )
|
|
goto indexBufferLockFailed;
|
|
}
|
|
|
|
// Unbind this bad boy if we've currently got it bound
|
|
if ( g_pLastIndexBuffer == m_pIndexBuffer )
|
|
{
|
|
Dx9Device()->SetIndices( NULL );
|
|
g_pLastIndex = NULL;
|
|
g_pLastIndexBuffer = NULL;
|
|
}
|
|
|
|
// Check to see if we have enough memory
|
|
nMemoryRequired = nMaxIndexCount * IndexSize();
|
|
bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize );
|
|
|
|
nLockFlags = D3DLOCK_NOSYSLOCK;
|
|
if ( bAppend )
|
|
{
|
|
// Can't have the first lock after a flush be an appending lock
|
|
Assert( !m_bFlush );
|
|
|
|
// If we're appending and we don't have enough room, then puke!
|
|
if ( !bHasEnoughMemory || m_bFlush )
|
|
goto indexBufferLockFailed;
|
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
|
|
}
|
|
else
|
|
{
|
|
// If we're not appending, no overwrite unless we don't have enough room
|
|
// If we're a static buffer, always discard if we're not appending
|
|
if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic )
|
|
{
|
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
|
|
}
|
|
else
|
|
{
|
|
if ( m_bIsDynamic )
|
|
{
|
|
nLockFlags |= D3DLOCK_DISCARD;
|
|
}
|
|
m_nFirstUnwrittenOffset = 0;
|
|
m_bFlush = false;
|
|
}
|
|
}
|
|
|
|
#if !defined( _X360 )
|
|
#if SHADERAPI_NO_D3DDeviceWrapper
|
|
hr = m_pIndexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags );
|
|
#else
|
|
hr = Dx9Device()->Lock( m_pIndexBuffer, m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags );
|
|
#endif
|
|
#else
|
|
hr = m_pIndexBuffer->Lock( 0, 0, &pLockedData, nLockFlags );
|
|
pLockedData = ( ( unsigned char * )pLockedData + m_nFirstUnwrittenOffset );
|
|
#endif
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
FailedLock( "Failed to lock index buffer in CIndexBufferDx8::LockIndexBuffer\n" );
|
|
goto indexBufferLockFailed;
|
|
}
|
|
|
|
desc.m_pIndices = (unsigned short*)( pLockedData );
|
|
desc.m_nIndexSize = IndexSize() >> 1;
|
|
if ( g_pHardwareConfig->SupportsStreamOffset() )
|
|
{
|
|
desc.m_nFirstIndex = 0;
|
|
desc.m_nOffset = m_nFirstUnwrittenOffset;
|
|
}
|
|
else
|
|
{
|
|
desc.m_nFirstIndex = m_nFirstUnwrittenOffset / IndexSize();
|
|
Assert( (int)( desc.m_nFirstIndex * IndexSize() ) == m_nFirstUnwrittenOffset );
|
|
desc.m_nOffset = 0;
|
|
}
|
|
m_bIsLocked = true;
|
|
|
|
#ifdef CHECK_INDICES
|
|
m_nLockIndexBufferSize = nMemoryRequired;
|
|
m_pLockIndexBuffer = desc.m_pIndices;
|
|
m_nLockIndexOffset = m_nFirstUnwrittenOffset;
|
|
#endif // CHECK_INDICES
|
|
|
|
return true;
|
|
|
|
indexBufferLockFailed:
|
|
g_ShaderMutex.Unlock();
|
|
|
|
// Set up a bogus index descriptor
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
desc.m_nFirstIndex = 0;
|
|
desc.m_nOffset = 0;
|
|
return false;
|
|
}
|
|
|
|
void CIndexBufferDx8::Unlock( int nWrittenIndexCount, IndexDesc_t &desc )
|
|
{
|
|
Assert( nWrittenIndexCount <= m_nIndexCount );
|
|
|
|
// NOTE: This can happen if another application finishes
|
|
// initializing during the construction of a mesh
|
|
if ( !m_bIsLocked )
|
|
return;
|
|
|
|
#ifdef CHECK_INDICES
|
|
memcpy( (unsigned char*)m_pShadowIndices + m_nLockIndexOffset, m_pLockIndexBuffer, nWrittenIndexCount * IndexSize() );
|
|
#endif // CHECK_INDICES
|
|
|
|
if ( m_pIndexBuffer )
|
|
{
|
|
#if SHADERAPI_NO_D3DDeviceWrapper
|
|
m_pIndexBuffer->Unlock();
|
|
#else
|
|
Dx9Device()->Unlock( m_pIndexBuffer );
|
|
#endif
|
|
}
|
|
|
|
m_nFirstUnwrittenOffset += nWrittenIndexCount * IndexSize();
|
|
m_bIsLocked = false;
|
|
g_ShaderMutex.Unlock();
|
|
}
|
|
|
|
|
|
void CIndexBufferDx8::SetIndexStreamState( int firstVertexIdx )
|
|
{
|
|
if ( g_pLastIndex || g_pLastIndexBuffer != m_pIndexBuffer || ( IsGameConsole() && ( IsDynamic() || IsExternal() ) ) )
|
|
{
|
|
D3DSetIndices( m_pIndexBuffer );
|
|
HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastIndex = NULL;
|
|
g_LastVertexIdx = -1;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Vertex Buffer implementations begin here
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// globals
|
|
//-----------------------------------------------------------------------------
|
|
#ifdef _DEBUG
|
|
int CVertexBufferDx8::s_nBufferCount = 0;
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor
|
|
//-----------------------------------------------------------------------------
|
|
CVertexBufferDx8::CVertexBufferDx8( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroupName ) :
|
|
BaseClass( pBudgetGroupName )
|
|
{
|
|
// Debugger();
|
|
Assert( nVertexCount != 0 );
|
|
|
|
m_pVertexBuffer = NULL;
|
|
m_VertexFormat = fmt;
|
|
m_nVertexCount = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? 0 : nVertexCount;
|
|
m_nBufferSize = ( fmt == VERTEX_FORMAT_UNKNOWN ) ? nVertexCount : nVertexCount * VertexSize();
|
|
m_nFirstUnwrittenOffset = 0;
|
|
m_bIsLocked = false;
|
|
m_bIsDynamic = ( type == SHADER_BUFFER_TYPE_DYNAMIC ) || ( type == SHADER_BUFFER_TYPE_DYNAMIC_TEMP );
|
|
m_bFlush = false;
|
|
|
|
#ifdef VPROF_ENABLED
|
|
if ( !m_bIsDynamic )
|
|
{
|
|
char name[256];
|
|
V_strcpy_safe( name, "TexGroup_global_" );
|
|
V_strcat_safe( name, pBudgetGroupName, sizeof(name) );
|
|
m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_GLOBAL );
|
|
|
|
V_strcpy_safe( name, "TexGroup_frame_" );
|
|
V_strcat_safe( name, pBudgetGroupName, sizeof(name) );
|
|
m_pFrameCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_PER_FRAME );
|
|
}
|
|
else
|
|
{
|
|
m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER, COUNTER_GROUP_TEXTURE_GLOBAL );
|
|
m_pFrameCounter = NULL;
|
|
}
|
|
m_nVProfFrame = -1;
|
|
#endif
|
|
}
|
|
|
|
CVertexBufferDx8::~CVertexBufferDx8()
|
|
{
|
|
Free();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the vertex size
|
|
//-----------------------------------------------------------------------------
|
|
inline int CVertexBufferDx8::VertexSize() const
|
|
{
|
|
Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN );
|
|
return VertexFormatSize( m_VertexFormat );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates, destroys the vertex buffer
|
|
//-----------------------------------------------------------------------------
|
|
bool CVertexBufferDx8::Allocate()
|
|
{
|
|
Assert( !m_pVertexBuffer );
|
|
m_nFirstUnwrittenOffset = 0;
|
|
|
|
D3DPOOL pool = D3DPOOL_MANAGED;
|
|
DWORD usage = D3DUSAGE_WRITEONLY;
|
|
if ( m_bIsDynamic )
|
|
{
|
|
usage |= D3DUSAGE_DYNAMIC;
|
|
pool = D3DPOOL_DEFAULT;
|
|
|
|
//
|
|
// UNDONE: Don't call this since GetVertexFormat will assert if m_bIsDynamic is set, and
|
|
// dynamic meshes shouldn't really have a fixed vertex format anyway.
|
|
//
|
|
// Dynamic meshes should never be compressed (slows down writing to them)
|
|
// Assert( CompressionType( GetVertexFormat() ) == VERTEX_COMPRESSION_NONE );
|
|
}
|
|
|
|
HRESULT hr = Dx9Device()->CreateVertexBuffer(
|
|
m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL );
|
|
|
|
#if !defined( _X360 )
|
|
if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) )
|
|
{
|
|
// Don't have the memory for this. Try flushing all managed resources
|
|
// out of vid mem and try again.
|
|
// FIXME: need to record this
|
|
Dx9Device()->EvictManagedResources();
|
|
hr = Dx9Device()->CreateVertexBuffer(
|
|
m_nBufferSize, usage, 0, pool, &m_pVertexBuffer, NULL );
|
|
}
|
|
#endif // !X360
|
|
|
|
if ( FAILED(hr) || ( m_pVertexBuffer == NULL ) )
|
|
{
|
|
Warning( "CVertexBufferDx8::Allocate: CreateVertexBuffer failed!\n" );
|
|
return false;
|
|
}
|
|
|
|
// Track VB allocations
|
|
g_VBAllocTracker->CountVB( m_pVertexBuffer, m_bIsDynamic, m_nBufferSize, m_bIsDynamic ? 0 : VertexSize(), m_VertexFormat );
|
|
|
|
#ifdef VPROF_ENABLED
|
|
if ( IsGameConsole() || !m_bIsDynamic )
|
|
{
|
|
Assert( m_pGlobalCounter );
|
|
*m_pGlobalCounter += m_nBufferSize;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
++s_nBufferCount;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void CVertexBufferDx8::Free()
|
|
{
|
|
// FIXME: Unlock(0);
|
|
if ( m_pVertexBuffer )
|
|
{
|
|
#ifdef _DEBUG
|
|
--s_nBufferCount;
|
|
#endif
|
|
|
|
// Track VB allocations
|
|
g_VBAllocTracker->UnCountVB( m_pVertexBuffer );
|
|
|
|
#ifdef VPROF_ENABLED
|
|
if ( IsGameConsole() || !m_bIsDynamic )
|
|
{
|
|
Assert( m_pGlobalCounter );
|
|
*m_pGlobalCounter -= m_nBufferSize;
|
|
}
|
|
#endif
|
|
|
|
#if SHADERAPI_NO_D3DDeviceWrapper
|
|
m_pVertexBuffer->Release();
|
|
#else
|
|
Dx9Device()->Release( m_pVertexBuffer );
|
|
#endif
|
|
m_pVertexBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Vertex buffer information
|
|
//-----------------------------------------------------------------------------
|
|
int CVertexBufferDx8::VertexCount() const
|
|
{
|
|
Assert( !m_bIsDynamic );
|
|
return m_nVertexCount;
|
|
}
|
|
|
|
VertexFormat_t CVertexBufferDx8::GetVertexFormat() const
|
|
{
|
|
Assert( !m_bIsDynamic );
|
|
return m_VertexFormat;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns true if the buffer is dynamic
|
|
//-----------------------------------------------------------------------------
|
|
bool CVertexBufferDx8::IsDynamic() const
|
|
{
|
|
return m_bIsDynamic;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Only used by dynamic buffers, indicates the next lock should perform a discard.
|
|
//-----------------------------------------------------------------------------
|
|
void CVertexBufferDx8::Flush()
|
|
{
|
|
// This strange-looking line makes a flush only occur if the buffer is dynamic.
|
|
m_bFlush = m_bIsDynamic;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the D3D buffer
|
|
//-----------------------------------------------------------------------------
|
|
IDirect3DVertexBuffer9* CVertexBufferDx8::GetDx9Buffer()
|
|
{
|
|
return m_pVertexBuffer;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Casts a dynamic buffer to be a particular vertex type
|
|
//-----------------------------------------------------------------------------
|
|
void CVertexBufferDx8::BeginCastBuffer( VertexFormat_t format )
|
|
{
|
|
Assert( format != MATERIAL_INDEX_FORMAT_UNKNOWN );
|
|
Assert( m_bIsDynamic && ( m_VertexFormat == 0 || m_VertexFormat == format ) );
|
|
if ( !m_bIsDynamic )
|
|
return;
|
|
|
|
m_VertexFormat = format;
|
|
int nVertexSize = VertexSize();
|
|
m_nVertexCount = m_nBufferSize / nVertexSize;
|
|
|
|
// snap current position up to the next position based on expected size
|
|
// so append can safely guarantee nooverwrite regardless of a format growth or shrinkage
|
|
if ( !g_pHardwareConfig->SupportsStreamOffset() )
|
|
{
|
|
m_nFirstUnwrittenOffset = ( m_nFirstUnwrittenOffset + nVertexSize - 1 ) / nVertexSize;
|
|
m_nFirstUnwrittenOffset *= nVertexSize;
|
|
if ( m_nFirstUnwrittenOffset > m_nBufferSize )
|
|
{
|
|
m_nFirstUnwrittenOffset = m_nBufferSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CVertexBufferDx8::EndCastBuffer( )
|
|
{
|
|
Assert( m_bIsDynamic && m_VertexFormat != 0 );
|
|
if ( !m_bIsDynamic )
|
|
return;
|
|
m_VertexFormat = 0;
|
|
m_nVertexCount = 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the number of vertices we can still write into the buffer
|
|
//-----------------------------------------------------------------------------
|
|
int CVertexBufferDx8::GetRoomRemaining() const
|
|
{
|
|
return ( m_nBufferSize - m_nFirstUnwrittenOffset ) / VertexSize();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the vertex buffer mesh
|
|
//-----------------------------------------------------------------------------
|
|
bool CVertexBufferDx8::Lock( int nMaxVertexCount, bool bAppend, VertexDesc_t &desc )
|
|
{
|
|
Assert( !m_bIsLocked && ( nMaxVertexCount != 0 ) && ( nMaxVertexCount <= m_nVertexCount ) );
|
|
Assert( m_VertexFormat != VERTEX_FORMAT_UNKNOWN );
|
|
|
|
// FIXME: Why do we need to sync matrices now?
|
|
ShaderUtil()->SyncMatrices();
|
|
g_ShaderMutex.Lock();
|
|
|
|
VPROF( "CVertexBufferDx8::Lock" );
|
|
|
|
void *pLockedData = NULL;
|
|
HRESULT hr;
|
|
int nMemoryRequired;
|
|
bool bHasEnoughMemory;
|
|
UINT nLockFlags;
|
|
|
|
// This can happen if the buffer was locked but a type wasn't bound
|
|
if ( m_VertexFormat == VERTEX_FORMAT_UNKNOWN )
|
|
goto vertexBufferLockFailed;
|
|
|
|
// Just give the app crap buffers to fill up while we're suppressed...
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() || ( nMaxVertexCount == 0 ) )
|
|
goto vertexBufferLockFailed;
|
|
|
|
// Did we ask for something too large?
|
|
if ( nMaxVertexCount > m_nVertexCount )
|
|
{
|
|
Warning( "Too many vertices for vertex buffer. . tell a programmer (%d>%d)\n", nMaxVertexCount, m_nVertexCount );
|
|
goto vertexBufferLockFailed;
|
|
}
|
|
|
|
// We might not have a buffer owing to alt-tab type stuff
|
|
if ( !m_pVertexBuffer )
|
|
{
|
|
if ( !Allocate() )
|
|
goto vertexBufferLockFailed;
|
|
}
|
|
|
|
// Check to see if we have enough memory
|
|
nMemoryRequired = nMaxVertexCount * VertexSize();
|
|
bHasEnoughMemory = ( m_nFirstUnwrittenOffset + nMemoryRequired <= m_nBufferSize );
|
|
|
|
nLockFlags = D3DLOCK_NOSYSLOCK;
|
|
if ( bAppend )
|
|
{
|
|
// Can't have the first lock after a flush be an appending lock
|
|
Assert( !m_bFlush );
|
|
|
|
// If we're appending and we don't have enough room, then puke!
|
|
if ( !bHasEnoughMemory || m_bFlush )
|
|
goto vertexBufferLockFailed;
|
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
|
|
}
|
|
else
|
|
{
|
|
// If we're not appending, no overwrite unless we don't have enough room
|
|
// If we're a static buffer, always discard if we're not appending
|
|
if ( !m_bFlush && bHasEnoughMemory && m_bIsDynamic )
|
|
{
|
|
nLockFlags |= ( m_nFirstUnwrittenOffset == 0 ) ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;
|
|
}
|
|
else
|
|
{
|
|
if ( m_bIsDynamic )
|
|
{
|
|
nLockFlags |= D3DLOCK_DISCARD;
|
|
}
|
|
m_nFirstUnwrittenOffset = 0;
|
|
m_bFlush = false;
|
|
}
|
|
}
|
|
|
|
#if !defined( _X360 )
|
|
#if SHADERAPI_NO_D3DDeviceWrapper
|
|
hr = m_pVertexBuffer->Lock( m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags );
|
|
#else
|
|
hr = Dx9Device()->Lock( m_pVertexBuffer, m_nFirstUnwrittenOffset, nMemoryRequired, &pLockedData, nLockFlags );
|
|
#endif
|
|
#else
|
|
hr = m_pVertexBuffer->Lock( 0, 0, &pLockedData, nLockFlags );
|
|
pLockedData = (unsigned char*)pLockedData + m_nFirstUnwrittenOffset;
|
|
#endif
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
// Check if paged pool is in critical state ( < 5% free )
|
|
PAGED_POOL_INFO_t ppi;
|
|
if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
|
|
( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
|
|
{
|
|
FailedLock( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com\n" );
|
|
}
|
|
else
|
|
{
|
|
FailedLock( "Failed to lock vertex buffer in CVertexBufferDx8::Lock\n" );
|
|
}
|
|
goto vertexBufferLockFailed;
|
|
}
|
|
|
|
ComputeVertexDescription( (unsigned char*)pLockedData, m_VertexFormat, desc );
|
|
if ( g_pHardwareConfig->SupportsStreamOffset() )
|
|
{
|
|
desc.m_nFirstVertex = 0;
|
|
desc.m_nOffset = m_nFirstUnwrittenOffset;
|
|
}
|
|
else
|
|
{
|
|
desc.m_nFirstVertex = m_nFirstUnwrittenOffset / VertexSize();
|
|
desc.m_nOffset = 0;
|
|
Assert( m_nFirstUnwrittenOffset == VertexSize() * desc.m_nFirstVertex );
|
|
}
|
|
m_bIsLocked = true;
|
|
return true;
|
|
|
|
vertexBufferLockFailed:
|
|
ComputeVertexDescription( 0, 0, desc );
|
|
desc.m_nFirstVertex = 0;
|
|
desc.m_nOffset = 0;
|
|
return false;
|
|
}
|
|
|
|
|
|
void CVertexBufferDx8::Unlock( int nWrittenVertexCount, VertexDesc_t &desc )
|
|
{
|
|
Assert( nWrittenVertexCount <= m_nVertexCount );
|
|
|
|
// NOTE: This can happen if another application finishes
|
|
// initializing during the construction of a mesh
|
|
if ( !m_bIsLocked )
|
|
return;
|
|
|
|
if ( m_pVertexBuffer )
|
|
{
|
|
#if SHADERAPI_NO_D3DDeviceWrapper
|
|
m_pVertexBuffer->Unlock();
|
|
#else
|
|
Dx9Device()->Unlock( m_pVertexBuffer );
|
|
#endif
|
|
}
|
|
|
|
m_nFirstUnwrittenOffset += nWrittenVertexCount * VertexSize();
|
|
m_bIsLocked = false;
|
|
g_ShaderMutex.Unlock();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Used to measure how much static buffer memory is touched each frame
|
|
//-----------------------------------------------------------------------------
|
|
void CVertexBufferDx8::HandlePerFrameTextureStats( int nFrame )
|
|
{
|
|
#ifdef VPROF_ENABLED
|
|
if ( m_nVProfFrame != nFrame && !m_bIsDynamic )
|
|
{
|
|
m_nVProfFrame = nFrame;
|
|
m_pFrameCounter += m_nBufferSize;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helpers with meshdescs...
|
|
//-----------------------------------------------------------------------------
|
|
// FIXME: add compression-agnostic read-accessors (which decompress and return by value, checking desc.m_CompressionType)
|
|
inline D3DXVECTOR3 &Position( MeshDesc_t const &desc, int vert )
|
|
{
|
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pPosition + vert * desc.m_VertexSize_Position );
|
|
}
|
|
|
|
inline float Wrinkle( MeshDesc_t const &desc, int vert )
|
|
{
|
|
return *(float*)((unsigned char*)desc.m_pWrinkle + vert * desc.m_VertexSize_Wrinkle );
|
|
}
|
|
|
|
inline D3DXVECTOR3 &BoneWeight( MeshDesc_t const &desc, int vert )
|
|
{
|
|
Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE );
|
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pBoneWeight + vert * desc.m_VertexSize_BoneWeight );
|
|
}
|
|
|
|
inline unsigned char *BoneIndex( MeshDesc_t const &desc, int vert )
|
|
{
|
|
return desc.m_pBoneMatrixIndex + vert * desc.m_VertexSize_BoneMatrixIndex;
|
|
}
|
|
|
|
inline D3DXVECTOR3 &Normal( MeshDesc_t const &desc, int vert )
|
|
{
|
|
Assert( desc.m_CompressionType == VERTEX_COMPRESSION_NONE );
|
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pNormal + vert * desc.m_VertexSize_Normal );
|
|
}
|
|
|
|
inline unsigned char *Color( MeshDesc_t const &desc, int vert )
|
|
{
|
|
return desc.m_pColor + vert * desc.m_VertexSize_Color;
|
|
}
|
|
|
|
inline D3DXVECTOR2 &TexCoord( MeshDesc_t const &desc, int vert, int stage )
|
|
{
|
|
return *(D3DXVECTOR2*)((unsigned char*)desc.m_pTexCoord[stage] + vert * desc.m_VertexSize_TexCoord[stage] );
|
|
}
|
|
|
|
inline D3DXVECTOR3 &TangentS( MeshDesc_t const &desc, int vert )
|
|
{
|
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentS + vert * desc.m_VertexSize_TangentS );
|
|
}
|
|
|
|
inline D3DXVECTOR3 &TangentT( MeshDesc_t const &desc, int vert )
|
|
{
|
|
return *(D3DXVECTOR3*)((unsigned char*)desc.m_pTangentT + vert * desc.m_VertexSize_TangentT );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Base mesh
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CBaseMeshDX8::CBaseMeshDX8() : m_VertexFormat(0)
|
|
{
|
|
m_bMeshLocked = false;
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_IsDrawing = false;
|
|
m_pMaterial = 0;
|
|
#endif
|
|
}
|
|
|
|
CBaseMeshDX8::~CBaseMeshDX8()
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// For debugging...
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseMeshDX8::DebugTrace() const
|
|
{
|
|
#ifdef DBGFLAG_ASSERT
|
|
if (m_pMaterial)
|
|
return m_pMaterial->PerformDebugTrace();
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
void CBaseMeshDX8::SetMaterial( IMaterial *pMaterial )
|
|
{
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_pMaterial = static_cast<IMaterialInternal *>(pMaterial);
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets, gets the vertex format
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseMeshDX8::SetVertexFormat( VertexFormat_t format, bool bHasVertexOverride, bool bHasIndexOverride )
|
|
{
|
|
m_VertexFormat = format;
|
|
}
|
|
|
|
VertexFormat_t CBaseMeshDX8::GetVertexFormat() const
|
|
{
|
|
return m_VertexFormat;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Do I need to reset the vertex format?
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const
|
|
{
|
|
return m_VertexFormat != fmt;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Do I have enough room?
|
|
//-----------------------------------------------------------------------------
|
|
bool CBaseMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const
|
|
{
|
|
// by default, we do
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Estimate the memory used
|
|
//-----------------------------------------------------------------------------
|
|
unsigned int CBaseMeshDX8::ComputeMemoryUsed()
|
|
{
|
|
unsigned size = 0;
|
|
|
|
if ( GetVertexBuffer() )
|
|
{
|
|
size += GetVertexBuffer()->AllocationSize();
|
|
}
|
|
|
|
if ( GetIndexBuffer() )
|
|
{
|
|
size += GetIndexBuffer()->AllocationSize();
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks mesh for modifying
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
// for the time being, disallow for most cases
|
|
Assert(0);
|
|
}
|
|
|
|
void CBaseMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
// for the time being, disallow for most cases
|
|
Assert(0);
|
|
}
|
|
|
|
void CBaseMeshDX8::ModifyEnd( MeshDesc_t& desc )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
// for the time being, disallow for most cases
|
|
Assert(0);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Begins a pass
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseMeshDX8::BeginPass( )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the render state and gets the drawing going
|
|
//-----------------------------------------------------------------------------
|
|
inline void CBaseMeshDX8::DrawMesh( const Vector4D *pVecDiffuseModulation )
|
|
{
|
|
#ifdef DBGFLAG_ASSERT
|
|
// Make sure we're not drawing...
|
|
Assert( !m_IsDrawing );
|
|
m_IsDrawing = true;
|
|
#endif
|
|
|
|
Assert( !g_pInstanceData );
|
|
|
|
MeshInstanceData_t instance;
|
|
CompiledLightingState_t *pCompiledLightingState;
|
|
InstanceInfo_t *pInstanceInfo;
|
|
TessellationMode_t nTessellationMode = TESSELLATION_MODE_DISABLED;
|
|
|
|
ShaderAPI()->GenerateNonInstanceRenderState( &instance, &pCompiledLightingState, &pInstanceInfo );
|
|
GetColorMesh( &instance.m_pColorBuffer, &instance.m_nColorVertexOffsetInBytes );
|
|
if ( pVecDiffuseModulation )
|
|
{
|
|
instance.m_DiffuseModulation = *pVecDiffuseModulation;
|
|
|
|
Vector4D matModulation;
|
|
IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial();
|
|
pMaterial->GetColorModulation( &matModulation[0], &matModulation[1], &matModulation[2] );
|
|
matModulation[3] = pMaterial->GetAlphaModulation();
|
|
|
|
instance.m_DiffuseModulation *= matModulation;
|
|
}
|
|
else
|
|
{
|
|
IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial();
|
|
pMaterial->GetColorModulation( &instance.m_DiffuseModulation[0], &instance.m_DiffuseModulation[1], &instance.m_DiffuseModulation[2] );
|
|
instance.m_DiffuseModulation[3] = pMaterial->GetAlphaModulation();
|
|
}
|
|
|
|
|
|
ShaderAPI()->SetTessellationMode( GetTessellationType() );
|
|
|
|
|
|
// This is going to cause RenderPass to get called a bunch
|
|
ShaderAPI()->DrawMesh( this, 1, &instance, CompressionType( GetVertexFormat() ), pCompiledLightingState, pInstanceInfo );
|
|
|
|
if ( nTessellationMode != TESSELLATION_MODE_DISABLED )
|
|
{
|
|
ShaderAPI()->SetTessellationMode( TESSELLATION_MODE_DISABLED );
|
|
}
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_IsDrawing = false;
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Spews the mesh data
|
|
//-----------------------------------------------------------------------------
|
|
void CBaseMeshDX8::Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
// This has regressed.
|
|
int i;
|
|
|
|
|
|
// FIXME: just fall back to the base class (CVertexBufferBase) version of this function!
|
|
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
if( m_pMaterial )
|
|
{
|
|
Plat_DebugString( ( const char * )m_pMaterial->GetName() );
|
|
Plat_DebugString( "\n" );
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
// This is needed so buffering can just use this
|
|
VertexFormat_t fmt = m_VertexFormat;
|
|
|
|
// Set up the vertex descriptor
|
|
MeshDesc_t desc = spewDesc;
|
|
|
|
char tempbuf[256];
|
|
char* temp = tempbuf;
|
|
sprintf( tempbuf,"\nVerts: (Vertex Format %llx)\n", fmt);
|
|
Plat_DebugString(tempbuf);
|
|
|
|
CVertexBufferBase::PrintVertexFormat( fmt );
|
|
|
|
int numBoneWeights = NumBoneWeights( fmt );
|
|
for ( i = 0; i < nVertexCount; ++i )
|
|
{
|
|
temp += sprintf( temp, "[%4d] ", i + desc.m_nFirstVertex );
|
|
if( fmt & VERTEX_POSITION )
|
|
{
|
|
D3DXVECTOR3& pos = Position( desc, i );
|
|
temp += sprintf(temp, "P %8.2f %8.2f %8.2f ",
|
|
pos[0], pos[1], pos[2]);
|
|
}
|
|
|
|
if ( fmt & VERTEX_WRINKLE )
|
|
{
|
|
float flWrinkle = Wrinkle( desc, i );
|
|
temp += sprintf(temp, "Wr %8.2f ",flWrinkle );
|
|
}
|
|
|
|
if (numBoneWeights > 0)
|
|
{
|
|
temp += sprintf(temp, "BW ");
|
|
float* pWeight = BoneWeight( desc, i );
|
|
for (int j = 0; j < numBoneWeights; ++j)
|
|
{
|
|
temp += sprintf(temp, "%1.2f ", pWeight[j]);
|
|
}
|
|
}
|
|
if ( fmt & VERTEX_BONE_INDEX )
|
|
{
|
|
unsigned char *pIndex = BoneIndex( desc, i );
|
|
temp += sprintf( temp, "BI %d %d %d %d ", ( int )pIndex[0], ( int )pIndex[1], ( int )pIndex[2], ( int )pIndex[3] );
|
|
Assert( uint( pIndex[0] ) < 16 );
|
|
Assert( uint( pIndex[1] ) < 16 );
|
|
Assert( uint( pIndex[2] ) < 16 );
|
|
Assert( uint( pIndex[3] ) < 16 );
|
|
}
|
|
|
|
if ( fmt & VERTEX_NORMAL )
|
|
{
|
|
D3DXVECTOR3& normal = Normal( desc, i );
|
|
temp += sprintf(temp, "N %1.2f %1.2f %1.2f ",
|
|
normal[0], normal[1], normal[2]);
|
|
}
|
|
|
|
if (fmt & VERTEX_COLOR)
|
|
{
|
|
unsigned char* pColor = Color( desc, i );
|
|
temp += sprintf(temp, "C b %3d g %3d r %3d a %3d ",
|
|
pColor[0], pColor[1], pColor[2], pColor[3]);
|
|
}
|
|
|
|
for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j)
|
|
{
|
|
if( TexCoordSize( j, fmt ) > 0)
|
|
{
|
|
D3DXVECTOR2& texcoord = TexCoord( desc, i, j );
|
|
temp += sprintf(temp, "T%d %.2f %.2f ", j,texcoord[0], texcoord[1]);
|
|
}
|
|
}
|
|
|
|
if (fmt & VERTEX_TANGENT_S)
|
|
{
|
|
D3DXVECTOR3& tangentS = TangentS( desc, i );
|
|
temp += sprintf(temp, "S %1.2f %1.2f %1.2f ",
|
|
tangentS[0], tangentS[1], tangentS[2]);
|
|
}
|
|
|
|
if (fmt & VERTEX_TANGENT_T)
|
|
{
|
|
D3DXVECTOR3& tangentT = TangentT( desc, i );
|
|
temp += sprintf(temp, "T %1.2f %1.2f %1.2f ",
|
|
tangentT[0], tangentT[1], tangentT[2]);
|
|
}
|
|
|
|
sprintf(temp,"\n");
|
|
Plat_DebugString(tempbuf);
|
|
temp = tempbuf;
|
|
}
|
|
|
|
sprintf( tempbuf,"\nIndices: %d\n", nIndexCount );
|
|
Plat_DebugString(tempbuf);
|
|
for ( i = 0; i < nIndexCount; ++i )
|
|
{
|
|
temp += sprintf( temp, "%d ", ( int )desc.m_pIndices[i] );
|
|
if ((i & 0x0F) == 0x0F)
|
|
{
|
|
sprintf( temp, "\n" );
|
|
Plat_DebugString(tempbuf);
|
|
tempbuf[0] = '\0';
|
|
temp = tempbuf;
|
|
}
|
|
}
|
|
sprintf(temp,"\n");
|
|
Plat_DebugString( tempbuf );
|
|
}
|
|
|
|
void CBaseMeshDX8::ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t &spewDesc )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
#ifdef VALIDATE_DEBUG
|
|
int i;
|
|
|
|
|
|
// FIXME: just fall back to the base class (CVertexBufferBase) version of this function!
|
|
|
|
|
|
// This is needed so buffering can just use this
|
|
VertexFormat_t fmt = m_pMaterial->GetVertexUsage();
|
|
|
|
// Set up the vertex descriptor
|
|
MeshDesc_t desc = spewDesc;
|
|
|
|
int numBoneWeights = NumBoneWeights( fmt );
|
|
for ( i = 0; i < nVertexCount; ++i )
|
|
{
|
|
if( fmt & VERTEX_POSITION )
|
|
{
|
|
D3DXVECTOR3& pos = Position( desc, i );
|
|
Assert( IsFinite( pos[0] ) && IsFinite( pos[1] ) && IsFinite( pos[2] ) );
|
|
}
|
|
if( fmt & VERTEX_WRINKLE )
|
|
{
|
|
float flWrinkle = Wrinkle( desc, i );
|
|
Assert( IsFinite( flWrinkle ) );
|
|
}
|
|
if (numBoneWeights > 0)
|
|
{
|
|
float* pWeight = BoneWeight( desc, i );
|
|
for (int j = 0; j < numBoneWeights; ++j)
|
|
{
|
|
Assert( pWeight[j] >= 0.0f && pWeight[j] <= 1.0f );
|
|
}
|
|
}
|
|
if( fmt & VERTEX_BONE_INDEX )
|
|
{
|
|
unsigned char *pIndex = BoneIndex( desc, i );
|
|
Assert( pIndex[0] >= 0 && pIndex[0] < 16 );
|
|
Assert( pIndex[1] >= 0 && pIndex[1] < 16 );
|
|
Assert( pIndex[2] >= 0 && pIndex[2] < 16 );
|
|
Assert( pIndex[3] >= 0 && pIndex[3] < 16 );
|
|
}
|
|
if( fmt & VERTEX_NORMAL )
|
|
{
|
|
D3DXVECTOR3& normal = Normal( desc, i );
|
|
Assert( normal[0] >= -1.05f && normal[0] <= 1.05f );
|
|
Assert( normal[1] >= -1.05f && normal[1] <= 1.05f );
|
|
Assert( normal[2] >= -1.05f && normal[2] <= 1.05f );
|
|
}
|
|
|
|
if (fmt & VERTEX_COLOR)
|
|
{
|
|
int* pColor = (int*)Color( desc, i );
|
|
Assert( *pColor != FLOAT32_NAN_BITS );
|
|
}
|
|
|
|
for (int j = 0; j < VERTEX_MAX_TEXTURE_COORDINATES; ++j)
|
|
{
|
|
if( TexCoordSize( j, fmt ) > 0)
|
|
{
|
|
D3DXVECTOR2& texcoord = TexCoord( desc, i, j );
|
|
Assert( IsFinite( texcoord[0] ) && IsFinite( texcoord[1] ) );
|
|
}
|
|
}
|
|
|
|
if (fmt & VERTEX_TANGENT_S)
|
|
{
|
|
D3DXVECTOR3& tangentS = TangentS( desc, i );
|
|
Assert( IsFinite( tangentS[0] ) && IsFinite( tangentS[1] ) && IsFinite( tangentS[2] ) );
|
|
}
|
|
|
|
if (fmt & VERTEX_TANGENT_T)
|
|
{
|
|
D3DXVECTOR3& tangentT = TangentT( desc, i );
|
|
Assert( IsFinite( tangentT[0] ) && IsFinite( tangentT[1] ) && IsFinite( tangentT[2] ) );
|
|
}
|
|
}
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
void CBaseMeshDX8::Draw( CPrimList *pLists, int nLists )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
Assert( !"CBaseMeshDX8::Draw(CPrimList, int): should never get here." );
|
|
}
|
|
|
|
|
|
// Copy verts and/or indices to a mesh builder. This only works for temp meshes!
|
|
void CBaseMeshDX8::CopyToMeshBuilder(
|
|
int iStartVert, // Which vertices to copy.
|
|
int nVerts,
|
|
int iStartIndex, // Which indices to copy.
|
|
int nIndices,
|
|
int indexOffset, // This is added to each index.
|
|
CMeshBuilder &builder )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
Assert( false );
|
|
Warning( "CopyToMeshBuilder called on something other than a temp mesh.\n" );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// static mesh
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CPrimList *CMeshDX8::s_pPrims;
|
|
int CMeshDX8::s_nPrims;
|
|
unsigned int CMeshDX8::s_FirstVertex;
|
|
unsigned int CMeshDX8::s_NumVertices;
|
|
|
|
#if ( PLATFORM_WINDOWS_PC || ( defined( _X360 ) ) )
|
|
#define PLATFORM_SUPPORTS_TRIANGLE_FANS 1
|
|
#else
|
|
#define PLATFORM_SUPPORTS_TRIANGLE_FANS 0
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the mode
|
|
//-----------------------------------------------------------------------------
|
|
inline D3DPRIMITIVETYPE ComputeMode( MaterialPrimitiveType_t type )
|
|
{
|
|
switch(type)
|
|
{
|
|
#ifdef _X360
|
|
case MATERIAL_INSTANCED_QUADS:
|
|
return D3DPT_QUADLIST;
|
|
#endif
|
|
|
|
case MATERIAL_POINTS:
|
|
return D3DPT_POINTLIST;
|
|
|
|
case MATERIAL_LINES:
|
|
return D3DPT_LINELIST;
|
|
|
|
case MATERIAL_TRIANGLES:
|
|
return D3DPT_TRIANGLELIST;
|
|
|
|
case MATERIAL_TRIANGLE_STRIP:
|
|
return D3DPT_TRIANGLESTRIP;
|
|
|
|
// Here, we expect to have the type set later. only works for static meshes
|
|
case MATERIAL_HETEROGENOUS:
|
|
return (D3DPRIMITIVETYPE)-1;
|
|
|
|
default:
|
|
Assert(0);
|
|
return (D3DPRIMITIVETYPE)-1;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor
|
|
//-----------------------------------------------------------------------------
|
|
CMeshDX8::CMeshDX8( const char *pTextureGroupName ) : m_NumVertices(0), m_NumIndices(0), m_pVertexBuffer(0),
|
|
m_pColorMesh( 0 ), m_nColorMeshVertOffsetInBytes( 0 ),
|
|
m_fmtStreamSpec( 0 ), m_pVbTexCoord1( 0 ),
|
|
m_pIndexBuffer(0), m_Type(MATERIAL_TRIANGLES), m_IsVBLocked(false),
|
|
m_IsIBLocked(false)
|
|
{
|
|
V_memset( m_arrRawHardwareDataStreams, 0, sizeof( m_arrRawHardwareDataStreams ) );
|
|
m_bHasRawHardwareDataStreams = false;
|
|
|
|
m_pTextureGroupName = pTextureGroupName;
|
|
m_Mode = ComputeMode(m_Type);
|
|
|
|
m_flexVertCount = 0;
|
|
m_bHasFlexVerts = false;
|
|
m_pFlexVertexBuffer = NULL;
|
|
m_nFlexVertOffsetInBytes = 0;
|
|
}
|
|
|
|
CMeshDX8::~CMeshDX8()
|
|
{
|
|
// Don't release the vertex buffer
|
|
if (!g_MeshMgr.IsDynamicMesh(this))
|
|
{
|
|
delete m_pVbTexCoord1;
|
|
delete m_pVertexBuffer;
|
|
delete m_pIndexBuffer;
|
|
|
|
for ( int k = 0; k < ARRAYSIZE( m_arrRawHardwareDataStreams ); ++ k )
|
|
{
|
|
if ( m_arrRawHardwareDataStreams[k] )
|
|
{
|
|
m_arrRawHardwareDataStreams[k]->Release();
|
|
m_arrRawHardwareDataStreams[k] = NULL;
|
|
}
|
|
}
|
|
m_bHasRawHardwareDataStreams = false;
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::SetFlexMesh( IMesh *pMesh, int nVertexOffsetInBytes )
|
|
{
|
|
if ( !ShaderUtil()->OnSetFlexMesh( this, pMesh, nVertexOffsetInBytes ) )
|
|
return;
|
|
|
|
LOCK_SHADERAPI();
|
|
m_nFlexVertOffsetInBytes = nVertexOffsetInBytes; // Offset into dynamic mesh (in bytes)
|
|
|
|
if ( pMesh )
|
|
{
|
|
m_flexVertCount = pMesh->VertexCount();
|
|
pMesh->MarkAsDrawn();
|
|
|
|
CBaseMeshDX8 *pBaseMesh = static_cast<CBaseMeshDX8 *>(pMesh);
|
|
m_pFlexVertexBuffer = pBaseMesh->GetVertexBuffer();
|
|
|
|
m_bHasFlexVerts = true;
|
|
}
|
|
else
|
|
{
|
|
m_flexVertCount = 0;
|
|
m_pFlexVertexBuffer = NULL;
|
|
m_bHasFlexVerts = false;
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::DisableFlexMesh( )
|
|
{
|
|
CMeshDX8::SetFlexMesh( NULL, 0 );
|
|
}
|
|
|
|
bool CMeshDX8::HasFlexMesh( ) const
|
|
{
|
|
LOCK_SHADERAPI();
|
|
return m_bHasFlexVerts;
|
|
}
|
|
|
|
void CMeshDX8::SetColorMesh( IMesh *pColorMesh, int nVertexOffsetInBytes )
|
|
{
|
|
if ( !ShaderUtil()->OnSetColorMesh( this, pColorMesh, nVertexOffsetInBytes ) )
|
|
return;
|
|
|
|
LOCK_SHADERAPI();
|
|
m_pColorMesh = ( CMeshDX8 * )pColorMesh; // dangerous conversion! garymcthack
|
|
m_nColorMeshVertOffsetInBytes = nVertexOffsetInBytes;
|
|
Assert( m_pColorMesh || ( nVertexOffsetInBytes == 0 ) );
|
|
|
|
#ifdef _DEBUG
|
|
if ( pColorMesh )
|
|
{
|
|
int nVertexCount = VertexCount();
|
|
int numVertsColorMesh = m_pColorMesh->VertexCount();
|
|
Assert( numVertsColorMesh >= nVertexCount );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void CMeshDX8::HandleLateCreation( )
|
|
{
|
|
if ( m_pVertexBuffer )
|
|
{
|
|
m_pVertexBuffer->HandleLateCreation();
|
|
}
|
|
if ( m_pIndexBuffer )
|
|
{
|
|
m_pIndexBuffer->HandleLateCreation();
|
|
}
|
|
if ( m_pFlexVertexBuffer )
|
|
{
|
|
m_pFlexVertexBuffer->HandleLateCreation();
|
|
}
|
|
|
|
if ( m_pColorMesh )
|
|
{
|
|
m_pColorMesh->HandleLateCreation();
|
|
}
|
|
}
|
|
|
|
|
|
bool CMeshDX8::HasColorMesh( ) const
|
|
{
|
|
LOCK_SHADERAPI();
|
|
return (m_pColorMesh != NULL);
|
|
}
|
|
|
|
void CMeshDX8::GetColorMesh( const IVertexBuffer** pMesh, int *pMeshVertexOffsetInBytes ) const
|
|
{
|
|
*pMesh = m_pColorMesh;
|
|
*pMeshVertexOffsetInBytes = m_nColorMeshVertOffsetInBytes;
|
|
}
|
|
|
|
VertexStreamSpec_t *CMeshDX8::GetVertexStreamSpec() const
|
|
{
|
|
return m_pVertexStreamSpec.Get();
|
|
}
|
|
|
|
void CMeshDX8::SetVertexStreamSpec( VertexStreamSpec_t *pStreamSpec )
|
|
{
|
|
m_pVertexStreamSpec.Delete();
|
|
m_fmtStreamSpec = 0;
|
|
|
|
int numSpecs = 0;
|
|
for ( VertexStreamSpec_t *pCount = pStreamSpec;
|
|
pCount && pCount->iVertexDataElement != VERTEX_FORMAT_UNKNOWN;
|
|
++ pCount, ++ numSpecs )
|
|
{
|
|
if ( pCount->iStreamSpec != VertexStreamSpec_t::STREAM_DEFAULT )
|
|
m_fmtStreamSpec |= pCount->iVertexDataElement;
|
|
}
|
|
|
|
if ( !numSpecs )
|
|
return;
|
|
|
|
m_pVertexStreamSpec.Attach( new VertexStreamSpec_t[ numSpecs + 1 ] );
|
|
memcpy( m_pVertexStreamSpec.Get(), pStreamSpec, (numSpecs + 1)*sizeof( VertexStreamSpec_t ) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/ unlocks the vertex buffer
|
|
//-----------------------------------------------------------------------------
|
|
bool CMeshDX8::Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc )
|
|
{
|
|
Assert( !m_IsVBLocked );
|
|
|
|
// Just give the app crap buffers to fill up while we're suppressed...
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() || (nVertexCount == 0))
|
|
{
|
|
// Set up the vertex descriptor
|
|
CVertexBufferBase::ComputeVertexDescription( 0, 0, desc );
|
|
desc.m_nFirstVertex = 0;
|
|
return false;
|
|
}
|
|
|
|
// Static vertex buffer case
|
|
if (!m_pVertexBuffer)
|
|
{
|
|
int size = g_MeshMgr.VertexFormatSize( m_VertexFormat &~ m_fmtStreamSpec );
|
|
m_pVertexBuffer = new CVertexBuffer( Dx9Device(), m_VertexFormat &~ m_fmtStreamSpec, 0, size, nVertexCount, m_pTextureGroupName, ShaderAPI()->UsingSoftwareVertexProcessing() );
|
|
if ( !m_pVertexBuffer )
|
|
{
|
|
MemOutOfMemory( sizeof(CVertexBuffer) );
|
|
}
|
|
|
|
if ( VertexStreamSpec_t *pTexCoord1 = FindVertexStreamSpec( VERTEX_TEXCOORD_SIZE( 1, 2 ), m_pVertexStreamSpec.Get() ) )
|
|
{
|
|
// TODO: actually create a full stream and allow modifications by the clients
|
|
DWORD dwVertexFormat = VERTEX_TEXCOORD_SIZE( 1, 2 );
|
|
int iVertexSize = 2 * sizeof( float );
|
|
int numVbEntries = 1;
|
|
m_pVbTexCoord1 = new CVertexBuffer( Dx9Device(),
|
|
dwVertexFormat, 0, iVertexSize,
|
|
numVbEntries, m_pTextureGroupName,
|
|
ShaderAPI()->UsingSoftwareVertexProcessing() );
|
|
if ( !m_pVbTexCoord1 )
|
|
{
|
|
MemOutOfMemory( sizeof(CVertexBuffer) );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Lock it baby
|
|
int nMaxVerts, nMaxIndices;
|
|
g_MeshMgr.GetMaxToRender( this, false, &nMaxVerts, &nMaxIndices );
|
|
if ( !g_pHardwareConfig->SupportsStreamOffset() )
|
|
{
|
|
// Without stream offset, we can't use VBs greater than 65535 verts (due to our using 16-bit indices)
|
|
Assert( nVertexCount <= nMaxVerts );
|
|
}
|
|
|
|
unsigned char *pVertexMemory = m_pVertexBuffer->Lock( nVertexCount, desc.m_nFirstVertex );
|
|
if ( !pVertexMemory )
|
|
{
|
|
// For debugging: when we get a dump crash of this, we'll know how many vertices were allocated
|
|
volatile int nVertexBuffer_VertexCount = m_pVertexBuffer->VertexCount(), nVertexBuffer_VertexSize = m_pVertexBuffer->VertexSize();
|
|
NOTE_UNUSED( nVertexBuffer_VertexCount );
|
|
NOTE_UNUSED( nVertexBuffer_VertexSize );
|
|
if ( nVertexCount > nMaxVerts )
|
|
{
|
|
Assert( 0 );
|
|
Error( "Too many verts for a dynamic vertex buffer (%d>%d) Tell a programmer to up VERTEX_BUFFER_SIZE.\n",
|
|
( int )nVertexCount, ( int )nMaxVerts );
|
|
}
|
|
else
|
|
{
|
|
// Check if paged pool is in critical state ( < 5% free )
|
|
PAGED_POOL_INFO_t ppi;
|
|
if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
|
|
( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
|
|
{
|
|
FailedLock( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com\n" );
|
|
}
|
|
else
|
|
{
|
|
Assert( 0 );
|
|
FailedLock( "failed to lock vertex buffer in CMeshDX8::LockVertexBuffer\n" );
|
|
}
|
|
}
|
|
CVertexBufferBase::ComputeVertexDescription( 0, 0, desc );
|
|
return false;
|
|
}
|
|
|
|
// Set up the vertex descriptor
|
|
CVertexBufferBase::ComputeVertexDescription( pVertexMemory, m_VertexFormat &~ m_fmtStreamSpec, desc );
|
|
m_IsVBLocked = true;
|
|
|
|
#ifdef RECORDING
|
|
m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize;
|
|
m_LockVertexBuffer = pVertexMemory;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMeshDX8::Unlock( int nVertexCount, VertexDesc_t& desc )
|
|
{
|
|
// NOTE: This can happen if another application finishes
|
|
// initializing during the construction of a mesh
|
|
if (!m_IsVBLocked)
|
|
return;
|
|
|
|
// This is recorded for debugging. . not sent to dx.
|
|
RECORD_COMMAND( DX8_SET_VERTEX_BUFFER_FORMAT, 2 );
|
|
RECORD_INT( m_pVertexBuffer->UID() );
|
|
RECORD_INT( m_VertexFormat &~ m_fmtStreamSpec );
|
|
|
|
RECORD_COMMAND( DX8_VERTEX_DATA, 3 );
|
|
RECORD_INT( m_pVertexBuffer->UID() );
|
|
RECORD_INT( m_LockVertexBufferSize );
|
|
RECORD_STRUCT( m_LockVertexBuffer, m_LockVertexBufferSize );
|
|
|
|
Assert(m_pVertexBuffer);
|
|
m_pVertexBuffer->Unlock(nVertexCount);
|
|
m_IsVBLocked = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the index buffer
|
|
//-----------------------------------------------------------------------------
|
|
int CMeshDX8::Lock( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t &desc, MeshBuffersAllocationSettings_t *pSettings )
|
|
{
|
|
Assert( !m_IsIBLocked );
|
|
|
|
// Just give the app crap buffers to fill up while we're suppressed...
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() || (nIndexCount == 0))
|
|
{
|
|
// Set up a bogus index descriptor
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
return 0;
|
|
}
|
|
|
|
// Static vertex buffer case
|
|
if (!m_pIndexBuffer)
|
|
{
|
|
m_pIndexBuffer = new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing(), false, pSettings );
|
|
}
|
|
|
|
desc.m_pIndices = m_pIndexBuffer->Lock( bReadOnly, nIndexCount, *(int*)( &desc.m_nFirstIndex ), nFirstIndex );
|
|
if( !desc.m_pIndices )
|
|
{
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
desc.m_nFirstIndex = 0;
|
|
|
|
// Check if paged pool is in critical state ( < 5% free )
|
|
PAGED_POOL_INFO_t ppi;
|
|
if ( ( SYSCALL_SUCCESS == Plat_GetPagedPoolInfo( &ppi ) ) &&
|
|
( ( ppi.numPagesFree * 20 ) < ( ppi.numPagesUsed + ppi.numPagesFree ) ) )
|
|
{
|
|
FailedLock( "Out of OS Paged Pool Memory! For more information, please see\nhttp://support.steampowered.com\n" );
|
|
}
|
|
else
|
|
{
|
|
Assert( 0 );
|
|
FailedLock( "failed to lock index buffer in CMeshDX8::LockIndexBuffer\n" );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
desc.m_nIndexSize = 1;
|
|
desc.m_nOffset = 0;
|
|
m_IsIBLocked = true;
|
|
|
|
#if defined( RECORDING ) || defined( CHECK_INDICES )
|
|
m_LockIndexBufferSize = nIndexCount * 2;
|
|
m_LockIndexBuffer = desc.m_pIndices;
|
|
#endif
|
|
|
|
return desc.m_nFirstIndex;
|
|
}
|
|
|
|
|
|
void CMeshDX8::Unlock( int nIndexCount, IndexDesc_t &desc )
|
|
{
|
|
// NOTE: This can happen if another application finishes
|
|
// initializing during the construction of a mesh
|
|
if (!m_IsIBLocked)
|
|
return;
|
|
|
|
RECORD_COMMAND( DX8_INDEX_DATA, 3 );
|
|
RECORD_INT( m_pIndexBuffer->UID() );
|
|
RECORD_INT( m_LockIndexBufferSize );
|
|
RECORD_STRUCT( m_LockIndexBuffer, m_LockIndexBufferSize );
|
|
|
|
Assert(m_pIndexBuffer);
|
|
|
|
#ifdef CHECK_INDICES
|
|
m_pIndexBuffer->UpdateShadowIndices( ( unsigned short * )m_LockIndexBuffer );
|
|
#endif // CHECK_INDICES
|
|
|
|
// Unlock, and indicate how many vertices we actually used
|
|
m_pIndexBuffer->Unlock(nIndexCount);
|
|
m_IsIBLocked = false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the entire mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc, MeshBuffersAllocationSettings_t *pSettings )
|
|
{
|
|
ShaderUtil()->SyncMatrices();
|
|
|
|
g_ShaderMutex.Lock();
|
|
VPROF( "CMeshDX8::LockMesh" );
|
|
Lock( nVertexCount, false, *static_cast<VertexDesc_t*>( &desc ) );
|
|
if ( m_Type != MATERIAL_POINTS )
|
|
{
|
|
Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ), pSettings );
|
|
}
|
|
else
|
|
{
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
}
|
|
|
|
CBaseMeshDX8::m_bMeshLocked = true;
|
|
}
|
|
|
|
|
|
void CMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
VPROF( "CMeshDX8::UnlockMesh" );
|
|
|
|
Assert( CBaseMeshDX8::m_bMeshLocked );
|
|
|
|
Unlock( nVertexCount, *static_cast<VertexDesc_t*>( &desc ) );
|
|
if ( m_Type != MATERIAL_POINTS )
|
|
{
|
|
Unlock( nIndexCount, *static_cast<IndexDesc_t*>( &desc ) );
|
|
}
|
|
|
|
// The actual # we wrote
|
|
m_NumVertices = nVertexCount;
|
|
m_NumIndices = nIndexCount;
|
|
|
|
CBaseMeshDX8::m_bMeshLocked = false;
|
|
g_ShaderMutex.Unlock();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks mesh for modifying
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
VPROF( "CMeshDX8::ModifyBegin" );
|
|
|
|
// Just give the app crap buffers to fill up while we're suppressed...
|
|
if ( g_pShaderDeviceDx8->IsDeactivated())
|
|
{
|
|
// Set up a bogus descriptor
|
|
g_MeshMgr.ComputeVertexDescription( 0, 0, desc );
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
return;
|
|
}
|
|
|
|
Assert( m_pVertexBuffer );
|
|
|
|
// Lock it baby
|
|
unsigned char* pVertexMemory = m_pVertexBuffer->Modify( bReadOnly, nFirstVertex, nVertexCount );
|
|
if ( pVertexMemory )
|
|
{
|
|
m_IsVBLocked = true;
|
|
g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat &~ m_fmtStreamSpec, desc );
|
|
|
|
#ifdef RECORDING
|
|
m_LockVertexBufferSize = nVertexCount * desc.m_ActualVertexSize;
|
|
m_LockVertexBuffer = pVertexMemory;
|
|
#endif
|
|
}
|
|
|
|
desc.m_nFirstVertex = nFirstVertex;
|
|
|
|
Lock( bReadOnly, nFirstIndex, nIndexCount, *static_cast<IndexDesc_t*>( &desc ) );
|
|
}
|
|
|
|
void CMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc );
|
|
}
|
|
|
|
void CMeshDX8::ModifyEnd( MeshDesc_t& desc )
|
|
{
|
|
VPROF( "CMeshDX8::ModifyEnd" );
|
|
Unlock( 0, *static_cast<IndexDesc_t*>( &desc ) );
|
|
Unlock( 0, *static_cast<VertexDesc_t*>( &desc ) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// returns the # of vertices (static meshes only)
|
|
//-----------------------------------------------------------------------------
|
|
int CMeshDX8::VertexCount() const
|
|
{
|
|
return m_pVertexBuffer ? m_pVertexBuffer->VertexCount() : 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// returns the # of indices
|
|
//-----------------------------------------------------------------------------
|
|
int CMeshDX8::IndexCount( ) const
|
|
{
|
|
return m_pIndexBuffer ? m_pIndexBuffer->IndexCount() : 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets up the vertex and index buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshDX8::UseIndexBuffer( CIndexBuffer* pBuffer )
|
|
{
|
|
m_pIndexBuffer = pBuffer;
|
|
}
|
|
|
|
void CMeshDX8::UseVertexBuffer( CVertexBuffer* pBuffer )
|
|
{
|
|
m_pVertexBuffer = pBuffer;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the primitive type
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type )
|
|
{
|
|
Assert( IsX360() || ( type != MATERIAL_INSTANCED_QUADS ) );
|
|
if ( !ShaderUtil()->OnSetPrimitiveType( this, type ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOCK_SHADERAPI();
|
|
m_Type = type;
|
|
m_Mode = ComputeMode( type );
|
|
}
|
|
|
|
MaterialPrimitiveType_t CMeshDX8::GetPrimitiveType( ) const
|
|
{
|
|
return m_Type;
|
|
}
|
|
|
|
#if ENABLE_TESSELLATION
|
|
TessellationMode_t CMeshDX8::GetTessellationType() const
|
|
{
|
|
switch( GetPrimitiveType() )
|
|
{
|
|
case MATERIAL_SUBD_QUADS_EXTRA:
|
|
return TESSELLATION_MODE_ACC_PATCHES_EXTRA;
|
|
case MATERIAL_SUBD_QUADS_REG:
|
|
return TESSELLATION_MODE_ACC_PATCHES_REG;
|
|
}
|
|
|
|
return TESSELLATION_MODE_DISABLED;
|
|
}
|
|
#endif
|
|
|
|
bool CMeshDX8::IsUsingVertexID() const
|
|
{
|
|
return ( g_pHardwareConfig->ActualHasFastVertexTextures() &&
|
|
ShaderAPI()->GetBoundMaterial()->IsUsingVertexID() &&
|
|
( GetTessellationType() > 0 || ( !m_pVertexBuffer->IsDynamic() && !m_pVertexBuffer->IsExternal() ) ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the number of primitives we're gonna draw
|
|
//-----------------------------------------------------------------------------
|
|
int CMeshDX8::NumPrimitives( int nVertexCount, int nIndexCount ) const
|
|
{
|
|
switch( m_Mode )
|
|
{
|
|
case D3DPT_POINTLIST:
|
|
return nVertexCount;
|
|
|
|
case D3DPT_LINELIST:
|
|
return nIndexCount / 2;
|
|
|
|
#ifndef DX_TO_GL_ABSTRACTION
|
|
case D3DPT_LINESTRIP:
|
|
return nIndexCount - 1;
|
|
#endif
|
|
|
|
case D3DPT_TRIANGLELIST:
|
|
return nIndexCount / 3;
|
|
|
|
case D3DPT_TRIANGLESTRIP:
|
|
return nIndexCount - 2;
|
|
|
|
#ifndef DX_TO_GL_ABSTRACTION
|
|
case D3DPT_TRIANGLEFAN: // We never use this anywhere else, so we override it to indicate quads
|
|
return nIndexCount / 4;
|
|
#endif
|
|
|
|
default:
|
|
// invalid, baby!
|
|
Assert(0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int NumPrimitives( MaterialPrimitiveType_t type, int nIndexCount )
|
|
{
|
|
switch( type )
|
|
{
|
|
case MATERIAL_LINES:
|
|
return nIndexCount / 2;
|
|
|
|
case MATERIAL_TRIANGLES:
|
|
return nIndexCount / 3;
|
|
|
|
case MATERIAL_TRIANGLE_STRIP:
|
|
return nIndexCount - 2;
|
|
|
|
case MATERIAL_SUBD_QUADS_EXTRA:
|
|
case MATERIAL_SUBD_QUADS_REG:
|
|
return nIndexCount / 4;
|
|
|
|
default:
|
|
// invalid, baby!
|
|
Assert(0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Checks if it's a valid format
|
|
//-----------------------------------------------------------------------------
|
|
#ifdef _DEBUG
|
|
static void OutputVertexFormat( VertexFormat_t format )
|
|
{
|
|
// FIXME: this is a duplicate of the function in meshdx8.cpp
|
|
VertexCompressionType_t compressionType = CompressionType( format );
|
|
|
|
if ( format & VERTEX_POSITION )
|
|
{
|
|
Warning( "VERTEX_POSITION|" );
|
|
}
|
|
if ( format & VERTEX_NORMAL )
|
|
{
|
|
if ( compressionType == VERTEX_COMPRESSION_ON )
|
|
Warning( "VERTEX_NORMAL[COMPRESSED]|" );
|
|
else
|
|
Warning( "VERTEX_NORMAL|" );
|
|
}
|
|
if ( format & VERTEX_COLOR )
|
|
{
|
|
Warning( "VERTEX_COLOR|" );
|
|
}
|
|
if ( format & VERTEX_SPECULAR )
|
|
{
|
|
Warning( "VERTEX_SPECULAR|" );
|
|
}
|
|
if ( format & VERTEX_TANGENT_S )
|
|
{
|
|
Warning( "VERTEX_TANGENT_S|" );
|
|
}
|
|
if ( format & VERTEX_TANGENT_T )
|
|
{
|
|
Warning( "VERTEX_TANGENT_T|" );
|
|
}
|
|
if ( format & VERTEX_BONE_INDEX )
|
|
{
|
|
Warning( "VERTEX_BONE_INDEX|" );
|
|
}
|
|
Warning( "\nBone weights: %d (%s)\n", NumBoneWeights( format ),
|
|
( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) );
|
|
Warning( "user data size: %d (%s)\n", UserDataSize( format ),
|
|
( CompressionType( format ) == VERTEX_COMPRESSION_ON ? "compressed" : "uncompressed" ) );
|
|
Warning( "num tex coords: %d\n", NumTextureCoordinates( format ) );
|
|
// NOTE: This doesn't print texcoord sizes.
|
|
}
|
|
#endif
|
|
|
|
static bool IsValidVertexFormat_Internal( VertexFormat_t meshFormat, IMaterial* pMaterial, VertexFormat_t materialFormat )
|
|
{
|
|
// the material format should match the vertex usage, unless another format is passed in
|
|
if ( materialFormat == VERTEX_FORMAT_INVALID )
|
|
{
|
|
Assert( pMaterial );
|
|
materialFormat = static_cast<IMaterialInternal*>( pMaterial )->GetVertexUsage() & ~( VERTEX_COLOR_STREAM_1 | VERTEX_FORMAT_USE_EXACT_FORMAT );
|
|
|
|
// Blat out unused fields
|
|
materialFormat &= ~g_MeshMgr.UnusedVertexFields();
|
|
int nUnusedTextureCoords = g_MeshMgr.UnusedTextureCoords();
|
|
for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i )
|
|
{
|
|
if ( nUnusedTextureCoords & ( 1 << i ) )
|
|
{
|
|
materialFormat &= ~VERTEX_TEXCOORD_MASK( i );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
materialFormat &= ~( VERTEX_COLOR_STREAM_1 | VERTEX_FORMAT_USE_EXACT_FORMAT );
|
|
}
|
|
|
|
bool bIsValid = (( VERTEX_FORMAT_FIELD_MASK & materialFormat ) & ( VERTEX_FORMAT_FIELD_MASK & ~meshFormat )) == 0;
|
|
|
|
if ( meshFormat & VERTEX_FORMAT_COMPRESSED )
|
|
{
|
|
// We shouldn't get compressed verts if this material doesn't support them!
|
|
if ( ( materialFormat & VERTEX_FORMAT_COMPRESSED ) == 0 )
|
|
{
|
|
static int numWarnings = 0;
|
|
if ( numWarnings++ == 0 )
|
|
{
|
|
// NOTE: ComputeVertexFormat() will make sure no materials support VERTEX_FORMAT_COMPRESSED
|
|
// if vertex compression is disabled in the config
|
|
if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE )
|
|
Warning( "ERROR: Compressed vertices in use but vertex compression is disabled (or not supported on this hardware)!\n" );
|
|
else
|
|
Warning( "ERROR: Compressed vertices in use but material does not support them!\n" );
|
|
}
|
|
Assert( 0 );
|
|
bIsValid = false;
|
|
}
|
|
}
|
|
|
|
bIsValid = bIsValid && UserDataSize( meshFormat ) >= UserDataSize( materialFormat );
|
|
|
|
for ( int i=0; i < VERTEX_MAX_TEXTURE_COORDINATES; i++ )
|
|
{
|
|
if ( TexCoordSize( i, meshFormat ) < TexCoordSize( i, materialFormat ) )
|
|
{
|
|
bIsValid = false;
|
|
}
|
|
}
|
|
|
|
// NOTE: It can totally be valid to have more weights than the current number of bones.
|
|
// The -1 here is because if we have N bones, we can have only (N-1) weights,
|
|
// since the Nth is implied (the weights sum to 1).
|
|
int nWeightCount = NumBoneWeights( meshFormat );
|
|
bIsValid = bIsValid && ( nWeightCount >= ( g_pShaderAPI->GetCurrentNumBones() - 1 ) );
|
|
|
|
#ifdef _DEBUG
|
|
if ( !bIsValid )
|
|
{
|
|
Warning( "Material Format:" );
|
|
if ( g_pShaderAPI->GetCurrentNumBones() > 0 )
|
|
{
|
|
materialFormat |= VERTEX_BONE_INDEX;
|
|
materialFormat &= ~VERTEX_BONE_WEIGHT_MASK;
|
|
materialFormat |= VERTEX_BONEWEIGHT( 2 );
|
|
}
|
|
|
|
OutputVertexFormat( materialFormat );
|
|
Warning( "Mesh Format:" );
|
|
OutputVertexFormat( meshFormat );
|
|
}
|
|
#endif
|
|
return bIsValid;
|
|
}
|
|
|
|
static inline bool IsValidVertexFormat( VertexFormat_t meshFormat, IMaterial* pMaterial, VertexFormat_t materialFormat = VERTEX_FORMAT_INVALID )
|
|
{
|
|
// FIXME: Make this a debug-only check on say 6th July 2007 (after a week or so's testing)
|
|
// (i.e. avoid the 360 release build perf. hit for when we ship)
|
|
bool bCheckCompression = ( meshFormat & VERTEX_FORMAT_COMPRESSED ) &&
|
|
( ( materialFormat == VERTEX_FORMAT_INVALID ) || ( ( materialFormat & VERTEX_FORMAT_COMPRESSED ) == 0 ) );
|
|
|
|
if ( !bCheckCompression && !IsPC() && !IsDebug() )
|
|
return true;
|
|
return IsValidVertexFormat_Internal( meshFormat, pMaterial, materialFormat );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stream source setting methods
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshDX8::SetVertexIDStreamState( int nIDOffsetBytes )
|
|
{
|
|
// FIXME: this method duplicates the code in CMeshMgr::SetVertexIDStreamState
|
|
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
bool bUsingVertexID = IsUsingVertexID();
|
|
if ( bUsingVertexID != g_bUsingVertexID || g_nLastVertexIDOffset != nIDOffsetBytes )
|
|
{
|
|
if ( bUsingVertexID )
|
|
{
|
|
// NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH
|
|
// It's because the indices (which are not 0 based for dynamic buffers)
|
|
// are accessing both the vertexID buffer + the regular vertex buffer.
|
|
// This *might* be fixable with baseVertexIndex?
|
|
|
|
// NOTE: At the moment, vertex id is only used for hw morphing. I've got it
|
|
// set up so that a shader that supports hw morphing always says it uses vertex id.
|
|
// If we ever use vertex id for something other than hw morphing, we're going
|
|
// to have to revisit how those shaders say they want to use vertex id
|
|
// or fix this some other way
|
|
|
|
// NOTE: SubDivivison surfaces are now using vertex id for the instanced patch case.
|
|
// These are dynamic buffers, so now we've bifurcated the VertexID code for dynamic buffers.
|
|
Assert( !g_pShaderAPI->IsHWMorphingEnabled() || !m_pVertexBuffer->IsDynamic() || GetTessellationType() > 0 );
|
|
|
|
CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( );
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( pVertexIDBuffer->UID() );
|
|
RECORD_INT( VertexStreamSpec_t::STREAM_MORPH );
|
|
RECORD_INT( nIDOffsetBytes );
|
|
RECORD_INT( pVertexIDBuffer->VertexSize() );
|
|
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_MORPH, pVertexIDBuffer->GetInterface(), nIDOffsetBytes, pVertexIDBuffer->VertexSize() );
|
|
pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
}
|
|
else
|
|
{
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 ); // vertex buffer id
|
|
RECORD_INT( VertexStreamSpec_t::STREAM_MORPH ); // stream
|
|
RECORD_INT( 0 ); // vertex offset
|
|
RECORD_INT( 0 ); // vertex size
|
|
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_MORPH, 0, 0, 0 );
|
|
}
|
|
g_bUsingVertexID = bUsingVertexID;
|
|
g_nLastVertexIDOffset = nIDOffsetBytes;
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel )
|
|
{
|
|
// NOTE: do we need this method in CMeshMgr::SetTessellationStreamState
|
|
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
bool bUsingPreTessPatches = ( GetTessellationType() > 0 );
|
|
if ( bUsingPreTessPatches != g_bUsingPreTessPatches )
|
|
{
|
|
if ( bUsingPreTessPatches )
|
|
{
|
|
// Patches for subdivision start at 1 ( 1 is 0 subdivisions )
|
|
iSubdivLevel --;
|
|
|
|
// Bind our patches VB to stream 0
|
|
CVertexBuffer *pPatchVB = g_MeshMgr.GetPreTessPatchVertexBuffer( iSubdivLevel );
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( pPatchVB->UID() );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( pPatchVB->VertexSize() );
|
|
D3DSetStreamSource( 0, pPatchVB->GetInterface(), 0, pPatchVB->VertexSize() );
|
|
pPatchVB->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
g_pLastVertex = pPatchVB;
|
|
g_nLastVertOffsetInBytes = 0;
|
|
|
|
// Override the index buffer with our patch index buffer
|
|
CIndexBuffer* pPatchIB = g_MeshMgr.GetPreTessPatchIndexBuffer( iSubdivLevel );
|
|
RECORD_COMMAND( DX8_SET_INDICES, 2 );
|
|
RECORD_INT( pPatchIB->UID() );
|
|
RECORD_INT( 0 );
|
|
Dx9Device()->SetIndices( pPatchIB->GetInterface() );
|
|
pPatchIB->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastIndex = pPatchIB;
|
|
g_pLastIndexBuffer = NULL;
|
|
g_LastVertexIdx = -1;
|
|
}
|
|
else
|
|
{
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 ); // vertex buffer id
|
|
RECORD_INT( VertexStreamSpec_t::STREAM_SUBDQUADS ); // stream
|
|
RECORD_INT( 0 ); // vertex offset
|
|
RECORD_INT( 0 ); // vertex size
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SUBDQUADS, 0, 0, 0 );
|
|
}
|
|
g_bUsingPreTessPatches = bUsingPreTessPatches;
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::SetCustomStreamsState()
|
|
{
|
|
if ( ( !g_pLastRawHardwareDataStream && !m_bHasRawHardwareDataStreams ) ||
|
|
( g_pLastRawHardwareDataStream == m_arrRawHardwareDataStreams ) )
|
|
{
|
|
// Case 1: No old streams set and this mesh has no hw data streams
|
|
// Case 2: Old streams set to the same streams that this mesh has
|
|
// Nothing to do here
|
|
}
|
|
else
|
|
{
|
|
LPDIRECT3DVERTEXBUFFER *arrRawStreams = m_bHasRawHardwareDataStreams ? m_arrRawHardwareDataStreams : NULL;
|
|
g_pLastRawHardwareDataStream = arrRawStreams;
|
|
#ifdef _PS3
|
|
Dx9Device()->SetRawHardwareDataStreams( arrRawStreams );
|
|
#endif
|
|
}
|
|
|
|
if ( m_pVertexStreamSpec.Get() != g_pLastStreamSpec )
|
|
{
|
|
if ( m_pVbTexCoord1 )
|
|
{
|
|
VertexStreamSpec_t *pTexCoord1 = FindVertexStreamSpec( VERTEX_TEXCOORD_SIZE( 1, 2 ), m_pVertexStreamSpec.Get() );
|
|
VertexStreamSpec_t::StreamSpec_t iStream = pTexCoord1 ? pTexCoord1->iStreamSpec : VertexStreamSpec_t::STREAM_UNIQUE_A;
|
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( m_pVbTexCoord1->UID() );
|
|
RECORD_INT( iStream );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
|
|
D3DSetStreamSource( iStream, m_pVbTexCoord1->GetInterface(), 0, 0 );
|
|
g_bCustomStreamsSet[ iStream ] = true;
|
|
|
|
m_pVbTexCoord1->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
}
|
|
else
|
|
{
|
|
VertexStreamSpec_t *pTexCoord1 = FindVertexStreamSpec( VERTEX_TEXCOORD_SIZE( 1, 2 ), m_pVertexStreamSpec.Get() );
|
|
VertexStreamSpec_t::StreamSpec_t iStream = pTexCoord1 ? pTexCoord1->iStreamSpec : VertexStreamSpec_t::STREAM_UNIQUE_A;
|
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 ); // vertex buffer id
|
|
RECORD_INT( iStream ); // stream
|
|
RECORD_INT( 0 ); // vertex offset
|
|
RECORD_INT( 0 ); // vertex size
|
|
|
|
D3DSetStreamSource( iStream, 0, 0, 0 );
|
|
g_bCustomStreamsSet[ iStream ] = false;
|
|
}
|
|
g_pLastStreamSpec = m_pVertexStreamSpec.Get();
|
|
}
|
|
}
|
|
|
|
void *CMeshDX8::AccessRawHardwareDataStream( uint8 nRawStreamIndex, uint32 numBytes, uint32 uiFlags, void *pvContext )
|
|
{
|
|
#ifdef _PS3
|
|
if ( nRawStreamIndex < ARRAYSIZE( m_arrRawHardwareDataStreams ) )
|
|
{
|
|
if ( !m_arrRawHardwareDataStreams[nRawStreamIndex] )
|
|
{
|
|
Dx9Device()->CreateVertexBuffer( numBytes, uiFlags, 0, D3DPOOL_MANAGED, &m_arrRawHardwareDataStreams[nRawStreamIndex], NULL );
|
|
if ( m_arrRawHardwareDataStreams[nRawStreamIndex] )
|
|
{
|
|
void *pbData = NULL;
|
|
m_arrRawHardwareDataStreams[nRawStreamIndex]->Lock( 0, numBytes, &pbData, D3DLOCK_NOOVERWRITE );
|
|
m_bHasRawHardwareDataStreams = true;
|
|
return pbData;
|
|
}
|
|
}
|
|
else if ( !numBytes && pvContext )
|
|
{
|
|
m_arrRawHardwareDataStreams[nRawStreamIndex]->Unlock();
|
|
return NULL;
|
|
}
|
|
}
|
|
Error( "<vitaliy> CMeshDX8::AccessRawHardwareDataStream unsupported codepath!\n" );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
inline void CMeshDX8::SetColorStreamState( )
|
|
{
|
|
CVertexBuffer *pColorVB = m_pColorMesh ? m_pColorMesh->GetVertexBuffer() : g_MeshMgr.GetEmptyColorBuffer();
|
|
int nVertOffset = m_pColorMesh ? m_nColorMeshVertOffsetInBytes : 0;
|
|
if ( ( pColorVB != g_pLastColorBuffer ) || ( nVertOffset != g_nLastColorMeshVertOffsetInBytes ) )
|
|
{
|
|
SetColorStreamState_Internal( pColorVB, nVertOffset );
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::SetColorStreamState_Internal( CVertexBuffer *pColorVB, int nVertOffset )
|
|
{
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( pColorVB->UID() );
|
|
RECORD_INT( VertexStreamSpec_t::STREAM_SPECULAR1 );
|
|
RECORD_INT( nVertOffset );
|
|
RECORD_INT( pColorVB->VertexSize() );
|
|
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SPECULAR1, pColorVB->GetInterface(), nVertOffset, pColorVB->VertexSize() );
|
|
pColorVB->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastColorBuffer = pColorVB;
|
|
g_nLastColorMeshVertOffsetInBytes = nVertOffset;
|
|
}
|
|
|
|
void CMeshDX8::SetVertexStreamState( int nVertOffsetInBytes, bool bIsRenderingInstances )
|
|
{
|
|
bool bUsingPreTessPatches = ( GetTessellationType() > 0 );
|
|
|
|
// Calls in here assume shader support...
|
|
if ( !bIsRenderingInstances && HasFlexMesh() )
|
|
{
|
|
// m_pFlexVertexBuffer is the flex buffer down inside the CMeshMgr singleton
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, m_pFlexVertexBuffer->GetInterface(), m_nFlexVertOffsetInBytes, m_pFlexVertexBuffer->VertexSize() );
|
|
|
|
// cFlexScale.x masks flex in vertex shader
|
|
float c[4] = { 1.0f, g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 92 ? 1.0f : 0.0f, 0.0f, 0.0f };
|
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
|
|
}
|
|
else if ( bUsingPreTessPatches )
|
|
{
|
|
// Override the original vertex buffer because we cannot have instance data in stream 0
|
|
Assert( m_pVertexBuffer );
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( m_pVertexBuffer->UID() );
|
|
RECORD_INT( VertexStreamSpec_t::STREAM_SUBDQUADS );
|
|
RECORD_INT( nVertOffsetInBytes );
|
|
RECORD_INT( m_pVertexBuffer->VertexSize() * 4 );
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SUBDQUADS, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() * 4 );
|
|
m_pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastVertex = NULL;
|
|
g_nLastVertOffsetInBytes = -1;
|
|
}
|
|
else if( GetTessellationType() == 0 )
|
|
{
|
|
Assert( nVertOffsetInBytes == 0 );
|
|
Assert( m_pVertexBuffer );
|
|
|
|
// HACK...point stream 2 at the same VB which is bound to stream 0...
|
|
// NOTE: D3D debug DLLs will RIP if stream 0 has a smaller stride than the largest
|
|
// offset in the stream 2 vertex decl elements (which are position(12)+wrinkle(4)+normal(12))
|
|
// If this fires, go find the material/shader which is requesting a really 'thin'
|
|
// stream 0 vertex, and fatten it up slightly (e.g. add a D3DCOLOR element)
|
|
int minimumStreamZeroStride = 4 * sizeof( float );
|
|
Assert( m_pVertexBuffer->VertexSize() >= minimumStreamZeroStride );
|
|
if ( m_pVertexBuffer->VertexSize() < minimumStreamZeroStride )
|
|
{
|
|
static bool bWarned = false;
|
|
if( !bWarned )
|
|
{
|
|
Warning( "Shader specifying too-thin vertex format, should be at least %d bytes! (Supressing furthur warnings)\n", minimumStreamZeroStride );
|
|
bWarned = true;
|
|
}
|
|
}
|
|
|
|
// Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 0 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia)
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 );
|
|
//D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() );
|
|
|
|
// cFlexScale.x masks flex in vertex shader
|
|
if ( !bIsRenderingInstances )
|
|
{
|
|
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
|
|
}
|
|
}
|
|
|
|
// MESHFIXME: Make sure this jives between the mesh/ib/vb version.
|
|
if( !bUsingPreTessPatches )
|
|
{
|
|
// [will] - Added defined( OSX ) because Scaleform renderer circumvents the MeshMgr and changes internal vertex buffer, so we can't rely on caching it.
|
|
#if defined( _GAMECONSOLE ) || defined( OSX )
|
|
if ( ( g_pLastVertex != m_pVertexBuffer ) || m_pVertexBuffer->IsDynamic() || m_pVertexBuffer->IsExternal() || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) )
|
|
#else
|
|
if ( ( g_pLastVertex != m_pVertexBuffer ) || ( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) )
|
|
#endif
|
|
{
|
|
Assert( m_pVertexBuffer );
|
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( m_pVertexBuffer->UID() );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( nVertOffsetInBytes );
|
|
RECORD_INT( m_pVertexBuffer->VertexSize() );
|
|
|
|
D3DSetStreamSource( 0, m_pVertexBuffer->GetInterface(), nVertOffsetInBytes, m_pVertexBuffer->VertexSize() );
|
|
m_pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastVertex = m_pVertexBuffer;
|
|
g_nLastVertOffsetInBytes = nVertOffsetInBytes;
|
|
}
|
|
}
|
|
|
|
if ( ( !g_pLastRawHardwareDataStream && !m_bHasRawHardwareDataStreams ) ||
|
|
( g_pLastRawHardwareDataStream == m_arrRawHardwareDataStreams ) )
|
|
{
|
|
// Case 1: No old streams set and this mesh has no hw data streams
|
|
// Case 2: Old streams set to the same streams that this mesh has
|
|
// Nothing to do here
|
|
}
|
|
else
|
|
{
|
|
LPDIRECT3DVERTEXBUFFER *arrRawStreams = m_bHasRawHardwareDataStreams ? m_arrRawHardwareDataStreams : NULL;
|
|
g_pLastRawHardwareDataStream = arrRawStreams;
|
|
#ifdef _PS3
|
|
Dx9Device()->SetRawHardwareDataStreams( arrRawStreams );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::SetIndexStreamState( int firstVertexIdx )
|
|
{
|
|
if( !( GetTessellationType() > 0 ) )
|
|
{
|
|
#ifdef _GAMECONSOLE
|
|
if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || m_pIndexBuffer->IsDynamic() || m_pIndexBuffer->IsExternal() || ( firstVertexIdx != g_LastVertexIdx ) )
|
|
#else
|
|
if ( ( g_pLastIndexBuffer != NULL ) || (g_pLastIndex != m_pIndexBuffer) || ( firstVertexIdx != g_LastVertexIdx ) )
|
|
#endif
|
|
{
|
|
Assert( m_pIndexBuffer );
|
|
|
|
RECORD_COMMAND( DX8_SET_INDICES, 2 );
|
|
RECORD_INT( m_pIndexBuffer->UID() );
|
|
RECORD_INT( firstVertexIdx );
|
|
|
|
D3DSetIndices( m_pIndexBuffer->GetInterface() );
|
|
m_pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
m_FirstIndex = firstVertexIdx;
|
|
|
|
g_pLastIndex = m_pIndexBuffer;
|
|
g_LastVertexIdx = firstVertexIdx;
|
|
}
|
|
}
|
|
}
|
|
|
|
static ConVar mat_tessellationlevel( "mat_tessellationlevel", "6", FCVAR_CHEAT );
|
|
|
|
bool CMeshDX8::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, int nIDOffsetBytes, VertexFormat_t vertexFormat )
|
|
{
|
|
// Can't set the state if we're deactivated
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() )
|
|
{
|
|
ResetMeshRenderState();
|
|
return false;
|
|
}
|
|
|
|
g_LastVertexFormat = vertexFormat;
|
|
int iSubdivLevel = (int)ceil( mat_tessellationlevel.GetFloat() );
|
|
|
|
if ( iSubdivLevel > MAX_TESS_DIVISIONS_PER_SIDE )
|
|
{
|
|
mat_tessellationlevel.SetValue( MAX_TESS_DIVISIONS_PER_SIDE );
|
|
}
|
|
|
|
if ( iSubdivLevel < 1 )
|
|
{
|
|
mat_tessellationlevel.SetValue( 1 );
|
|
}
|
|
|
|
iSubdivLevel = MIN( MAX_TESS_DIVISIONS_PER_SIDE, MAX( 1, iSubdivLevel ) );
|
|
|
|
SetVertexIDStreamState( nIDOffsetBytes );
|
|
SetColorStreamState();
|
|
SetCustomStreamsState();
|
|
SetVertexStreamState( nVertexOffsetInBytes, false );
|
|
SetIndexStreamState( nFirstVertexIdx );
|
|
SetTessellationStreamState( nVertexOffsetInBytes, iSubdivLevel );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Draws the static mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshDX8::DrawModulated( const Vector4D &diffuseModulation, int nFirstIndex, int nIndexCount )
|
|
{
|
|
if ( !ShaderUtil()->OnDrawMeshModulated( this, diffuseModulation, nFirstIndex, nIndexCount ) )
|
|
{
|
|
MarkAsDrawn();
|
|
return;
|
|
}
|
|
|
|
CPrimList primList;
|
|
if( nFirstIndex == -1 || nIndexCount == 0 )
|
|
{
|
|
primList.m_FirstIndex = 0;
|
|
primList.m_NumIndices = m_NumIndices;
|
|
}
|
|
else
|
|
{
|
|
primList.m_FirstIndex = nFirstIndex;
|
|
primList.m_NumIndices = nIndexCount;
|
|
}
|
|
DrawInternal( &diffuseModulation, &primList, 1 );
|
|
}
|
|
|
|
void CMeshDX8::Draw( int nFirstIndex, int nIndexCount )
|
|
{
|
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
|
|
{
|
|
MarkAsDrawn();
|
|
return;
|
|
}
|
|
|
|
CPrimList primList;
|
|
if( nFirstIndex == -1 || nIndexCount == 0 )
|
|
{
|
|
primList.m_FirstIndex = 0;
|
|
primList.m_NumIndices = m_NumIndices;
|
|
}
|
|
else
|
|
{
|
|
primList.m_FirstIndex = nFirstIndex;
|
|
primList.m_NumIndices = nIndexCount;
|
|
}
|
|
DrawInternal( NULL, &primList, 1 );
|
|
}
|
|
|
|
void CMeshDX8::Draw( CPrimList *pLists, int nLists )
|
|
{
|
|
if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) )
|
|
{
|
|
MarkAsDrawn();
|
|
return;
|
|
}
|
|
|
|
DrawInternal( NULL, pLists, nLists );
|
|
}
|
|
|
|
void CMeshDX8::DrawInternal( const Vector4D *pDiffuseModulation, CPrimList *pLists, int nLists )
|
|
{
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
HandleLateCreation();
|
|
#endif
|
|
|
|
// Make sure there's something to draw..
|
|
int i;
|
|
for ( i=0; i < nLists; i++ )
|
|
{
|
|
if ( pLists[i].m_NumIndices > 0 )
|
|
break;
|
|
}
|
|
if ( i == nLists )
|
|
return;
|
|
|
|
// can't do these in selection mode!
|
|
Assert( !ShaderAPI()->IsInSelectionMode() );
|
|
|
|
if ( !SetRenderState( 0, 0 ) )
|
|
return;
|
|
|
|
s_pPrims = pLists;
|
|
s_nPrims = nLists;
|
|
|
|
#ifdef _DEBUG
|
|
for ( i = 0; i < nLists; ++i)
|
|
{
|
|
Assert( pLists[i].m_NumIndices > 0 );
|
|
}
|
|
#endif
|
|
|
|
s_FirstVertex = 0;
|
|
s_NumVertices = m_pVertexBuffer->VertexCount();
|
|
|
|
DrawMesh( pDiffuseModulation );
|
|
}
|
|
|
|
|
|
#ifdef CHECK_INDICES
|
|
void CheckIndices( D3DPRIMITIVETYPE nMode, int nFirstVertex, int nVertexCount, int nBaseIndex, int nFirstIndex, int numPrimitives )
|
|
{
|
|
// g_pLastVertex - this is the current vertex buffer
|
|
// g_pLastColorBuffer - this is the current color mesh, if there is one.
|
|
// g_pLastIndex - this is the current index buffer.
|
|
// vertoffset : m_FirstIndex
|
|
|
|
// NOTE: This doesn't work for pure index buffers yet
|
|
if ( !g_pLastIndex || !g_pLastVertex )
|
|
return;
|
|
|
|
if( nMode == D3DPT_TRIANGLELIST || nMode == D3DPT_TRIANGLESTRIP )
|
|
{
|
|
Assert( nFirstIndex >= 0 && nFirstIndex < g_pLastIndex->IndexCount() );
|
|
int i;
|
|
for( i = 0; i < 2; i++ )
|
|
{
|
|
CVertexBuffer *pMesh;
|
|
if( i == 0 )
|
|
{
|
|
pMesh = g_pLastVertex;
|
|
Assert( pMesh );
|
|
}
|
|
else
|
|
{
|
|
if( !g_pLastColorBuffer || g_pLastColorBuffer == g_MeshMgr.GetEmptyColorBuffer() )
|
|
continue;
|
|
pMesh = g_pLastColorBuffer;
|
|
if( !pMesh )
|
|
continue;
|
|
}
|
|
Assert( nFirstVertex >= 0 &&
|
|
(int)( nFirstVertex + nBaseIndex ) < pMesh->VertexCount() );
|
|
int nIndexCount = 0;
|
|
if( nMode == D3DPT_TRIANGLELIST )
|
|
{
|
|
nIndexCount = numPrimitives * 3;
|
|
}
|
|
else if( nMode == D3DPT_TRIANGLESTRIP )
|
|
{
|
|
nIndexCount = numPrimitives + 2;
|
|
}
|
|
else
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
int j;
|
|
for( j = 0; j < nIndexCount; j++ )
|
|
{
|
|
int index = g_pLastIndex->GetShadowIndex( j + nFirstIndex );
|
|
Assert( index >= (int)nFirstVertex );
|
|
Assert( index < (int)(nFirstVertex + nVertexCount) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::CheckIndices( int nFirstIndex, int numPrimitives )
|
|
{
|
|
::CheckIndices( m_Mode, s_FirstVertex, s_NumVertices, m_FirstIndex, nFirstIndex, numPrimitives );
|
|
}
|
|
|
|
void CMeshDX8::CheckIndices( CPrimList *pPrim, int numPrimitives )
|
|
{
|
|
CheckIndices( pPrim->m_FirstIndex, numPrimitives );
|
|
}
|
|
#endif // CHECK_INDICES
|
|
|
|
void CMeshDX8::DrawPrims( const unsigned char *pInstanceCommandBuffer )
|
|
{
|
|
// Set up the "per-instance" render state for non-instanced draw calls
|
|
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, 0, false );
|
|
|
|
for ( int iPrim=0; iPrim < s_nPrims; iPrim++ )
|
|
{
|
|
CPrimList *pPrim = &s_pPrims[iPrim];
|
|
|
|
if ( pPrim->m_NumIndices == 0 )
|
|
continue;
|
|
|
|
int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices );
|
|
|
|
{
|
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" );
|
|
VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
|
|
VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives );
|
|
|
|
Dx9Device()->DrawIndexedPrimitive( m_Mode, m_FirstIndex,
|
|
s_FirstVertex, s_NumVertices, pPrim->m_FirstIndex, numPrimitives );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMeshDX8::RenderPass( const unsigned char *pInstanceCommandBuffer )
|
|
{
|
|
LOCK_SHADERAPI();
|
|
VPROF( "CMeshDX8::RenderPass" );
|
|
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
HandleLateCreation();
|
|
#endif
|
|
|
|
if ( g_nInstanceCount )
|
|
{
|
|
g_MeshMgr.RenderPassForInstances( pInstanceCommandBuffer );
|
|
return;
|
|
}
|
|
|
|
Assert( m_Type != MATERIAL_HETEROGENOUS );
|
|
|
|
// JasonM - skip this validation for subd quads
|
|
if ( m_Type != MATERIAL_SUBD_QUADS_EXTRA && m_Type != MATERIAL_SUBD_QUADS_REG )
|
|
{
|
|
// make sure the vertex format is a superset of the current material's vertex format...
|
|
if ( !IsValidVertexFormat( m_VertexFormat, ShaderAPI()->GetBoundMaterial(), g_LastVertexFormat ) )
|
|
{
|
|
Warning( "Material %s does not support vertex format used by the mesh (maybe missing fields or mismatched vertex compression?), mesh will not be rendered. Grab a programmer!\n",
|
|
ShaderAPI()->GetBoundMaterial()->GetName() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Set up the "per-instance" render state for non-instanced draw calls
|
|
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, 0, false );
|
|
|
|
for ( int iPrim=0; iPrim < s_nPrims; iPrim++ )
|
|
{
|
|
CPrimList *pPrim = &s_pPrims[iPrim];
|
|
|
|
if ( pPrim->m_NumIndices == 0 )
|
|
continue;
|
|
|
|
if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) )
|
|
{
|
|
// (For point/instanced-quad lists, we don't actually fill in indices, but we treat it as
|
|
// though there are indices for the list up until here).
|
|
Dx9Device()->DrawPrimitive( m_Mode, s_FirstVertex, pPrim->m_NumIndices );
|
|
}
|
|
else if ( m_Type == MATERIAL_SUBD_QUADS_EXTRA || m_Type == MATERIAL_SUBD_QUADS_REG )
|
|
{
|
|
//#if ( defined ( _X360 ) || defined ( DX_TO_GL_ABSTRACTION ) )
|
|
#if ( 1 )
|
|
AssertMsg( false, "MATERIAL_SUBD_QUADS are not supported" );
|
|
#else
|
|
Assert( ShaderAPI()->GetTessellationMode() != TESSELLATION_MODE_DISABLED );
|
|
|
|
Dx9Device()->SetTessellationLevel( MIN( MAX_TESS_DIVISIONS_PER_SIDE, MAX( 1, mat_tessellationlevel.GetFloat() ) ) );
|
|
Dx9Device()->DrawTessellatedIndexedPrimitive( m_FirstIndex, s_FirstVertex, s_NumVertices, pPrim->m_FirstIndex, pPrim->m_NumIndices / 4 );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices );
|
|
|
|
#ifdef CHECK_INDICES
|
|
CheckIndices( pPrim, numPrimitives );
|
|
#endif // CHECK_INDICES
|
|
{
|
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" );
|
|
VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
|
|
VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives );
|
|
|
|
#if defined( _X360 )
|
|
IDirect3DVertexShader9 *pVertShader = NULL;
|
|
Dx9Device()->GetVertexShader( &pVertShader );
|
|
if ( pVertShader != NULL )
|
|
{
|
|
pVertShader->Release(); // NOTE: IDirect3DDevice9::GetVertexShader increments the shader's internal refcount!
|
|
#endif // _X360
|
|
Dx9Device()->DrawIndexedPrimitive(
|
|
m_Mode, // Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method.
|
|
|
|
m_FirstIndex, // Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer.
|
|
|
|
s_FirstVertex, // Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex.
|
|
// The first Vertex in the vertexbuffer that we are currently using for the current batch.
|
|
|
|
s_NumVertices, // Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex.
|
|
|
|
pPrim->m_FirstIndex, // Index of the first index to use when accessing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer.
|
|
|
|
numPrimitives );// Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type.
|
|
#if defined( _X360 )
|
|
}
|
|
else
|
|
{
|
|
Warning( "CMeshDX8::RenderPass - Material \"%s\" has no vertex shader applied!\n", ShaderAPI()->GetBoundMaterial()->GetName() );
|
|
}
|
|
#endif // _X360
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( g_pLastVertex )
|
|
{
|
|
g_pLastVertex->MarkUsedInRendering();
|
|
}
|
|
|
|
if( g_pLastIndex )
|
|
{
|
|
g_pLastIndex->MarkUsedInRendering();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Dynamic mesh implementation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CDynamicMeshDX8::CDynamicMeshDX8() : CMeshDX8( "CDynamicMeshDX8" )
|
|
{
|
|
m_nBufferId = 0;
|
|
ResetVertexAndIndexCounts();
|
|
}
|
|
|
|
CDynamicMeshDX8::~CDynamicMeshDX8()
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initializes the dynamic mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::Init( int nBufferId )
|
|
{
|
|
m_nBufferId = nBufferId;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Resets buffering state
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::ResetVertexAndIndexCounts()
|
|
{
|
|
m_TotalVertices = m_TotalIndices = 0;
|
|
m_FirstIndex = m_nFirstVertex = -1;
|
|
m_HasDrawn = false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Resets the state in case of a task switch
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::Reset()
|
|
{
|
|
m_VertexFormat = 0;
|
|
m_pVertexBuffer = 0;
|
|
m_pIndexBuffer = 0;
|
|
ResetVertexAndIndexCounts();
|
|
|
|
// Force the render state to be updated next time
|
|
ResetMeshRenderState();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the vertex format associated with the dynamic mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::SetVertexFormat( VertexFormat_t format, bool bHasVertexOverride, bool bHasIndexOverride )
|
|
{
|
|
if ( g_pShaderDeviceDx8->IsDeactivated())
|
|
return;
|
|
|
|
if ( CompressionType( format ) != VERTEX_COMPRESSION_NONE )
|
|
{
|
|
// UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: CMeshBuilder gets slower)
|
|
Warning( "ERROR: dynamic meshes cannot use compressed vertices!\n" );
|
|
Assert( 0 );
|
|
format &= ~VERTEX_FORMAT_COMPRESSED;
|
|
}
|
|
|
|
format &= ~VERTEX_COLOR_STREAM_1;
|
|
|
|
if ((format != m_VertexFormat) || m_VertexOverride || m_IndexOverride)
|
|
{
|
|
m_VertexFormat = format;
|
|
|
|
if ( !bHasVertexOverride )
|
|
{
|
|
UseVertexBuffer( g_MeshMgr.FindOrCreateVertexBuffer( m_nBufferId, format ) );
|
|
m_VertexOverride = false;
|
|
}
|
|
|
|
if ( m_nBufferId == 0 && !bHasIndexOverride )
|
|
{
|
|
UseIndexBuffer( g_MeshMgr.GetDynamicIndexBufferInternal() );
|
|
m_IndexOverride = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDynamicMeshDX8::OverrideVertexBuffer( CVertexBuffer* pVertexBuffer )
|
|
{
|
|
UseVertexBuffer( pVertexBuffer );
|
|
m_VertexOverride = true;
|
|
}
|
|
|
|
void CDynamicMeshDX8::OverrideIndexBuffer( CIndexBuffer* pIndexBuffer )
|
|
{
|
|
UseIndexBuffer( pIndexBuffer );
|
|
m_IndexOverride = true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Do I need to reset the vertex format?
|
|
//-----------------------------------------------------------------------------
|
|
bool CDynamicMeshDX8::NeedsVertexFormatReset( VertexFormat_t fmt ) const
|
|
{
|
|
return m_VertexOverride || m_IndexOverride || (m_VertexFormat != fmt);
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the entire mesh
|
|
//-----------------------------------------------------------------------------
|
|
bool CDynamicMeshDX8::HasEnoughRoom( int nVertexCount, int nIndexCount ) const
|
|
{
|
|
Assert( m_pVertexBuffer != NULL );
|
|
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() )
|
|
return false;
|
|
|
|
// We need space in both the vertex and index buffer
|
|
return m_pVertexBuffer->HasEnoughRoom( nVertexCount ) &&
|
|
m_pIndexBuffer->HasEnoughRoom( nIndexCount );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// returns the number of indices in the mesh
|
|
//-----------------------------------------------------------------------------
|
|
int CDynamicMeshDX8::IndexCount( ) const
|
|
{
|
|
return m_TotalIndices;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Operation to do pre-lock (only called for buffered meshes)
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::PreLock()
|
|
{
|
|
if (m_HasDrawn)
|
|
{
|
|
// Start again then
|
|
ResetVertexAndIndexCounts();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the entire mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc, MeshBuffersAllocationSettings_t *pSettings )
|
|
{
|
|
ShaderUtil()->SyncMatrices();
|
|
|
|
g_ShaderMutex.Lock();
|
|
|
|
// Yes, this may well also be called from BufferedMesh but that's ok
|
|
PreLock();
|
|
|
|
if (m_VertexOverride)
|
|
{
|
|
nVertexCount = 0;
|
|
}
|
|
|
|
if (m_IndexOverride)
|
|
{
|
|
nIndexCount = 0;
|
|
}
|
|
|
|
Lock( nVertexCount, false, *static_cast<VertexDesc_t*>( &desc ) );
|
|
if (m_nFirstVertex < 0)
|
|
{
|
|
m_nFirstVertex = desc.m_nFirstVertex;
|
|
}
|
|
|
|
// When we're using a static index buffer or a flex mesh, the indices assume vertices start at 0
|
|
if ( m_IndexOverride || HasFlexMesh() )
|
|
{
|
|
desc.m_nFirstVertex -= m_nFirstVertex;
|
|
}
|
|
|
|
// Don't add indices for points; DrawIndexedPrimitive not supported for them.
|
|
if ( m_Type != MATERIAL_POINTS && m_Type != MATERIAL_INSTANCED_QUADS )
|
|
{
|
|
int nFirstIndex = Lock( false, -1, nIndexCount, *static_cast<IndexDesc_t*>( &desc ), pSettings );
|
|
if (m_FirstIndex < 0)
|
|
{
|
|
m_FirstIndex = nFirstIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
desc.m_nFirstIndex = 0;
|
|
}
|
|
|
|
CBaseMeshDX8::m_bMeshLocked = true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Unlocks the mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
m_TotalVertices += nVertexCount;
|
|
m_TotalIndices += nIndexCount;
|
|
|
|
// if (DebugTrace())
|
|
// {
|
|
// Spew( nVertexCount, nIndexCount, desc );
|
|
// }
|
|
|
|
CMeshDX8::UnlockMesh( nVertexCount, nIndexCount, desc );
|
|
|
|
// This is handled in the CMeshDX8::UnlockMesh above.
|
|
//CBaseMeshDX8::m_bMeshLocked = false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Draws it
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::DrawInternal( const Vector4D *pVecDiffuseModulation, int nFirstIndex, int nIndexCount )
|
|
{
|
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
|
|
{
|
|
MarkAsDrawn();
|
|
return;
|
|
}
|
|
|
|
VPROF( "CDynamicMeshDX8::Draw" );
|
|
|
|
m_HasDrawn = true;
|
|
|
|
if (m_IndexOverride || m_VertexOverride ||
|
|
( ( m_TotalVertices > 0 ) && ( m_TotalIndices > 0 || m_Type == MATERIAL_POINTS || m_Type == MATERIAL_INSTANCED_QUADS ) ) )
|
|
{
|
|
Assert( !m_IsDrawing );
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
HandleLateCreation();
|
|
#endif
|
|
|
|
// only have a non-zero first vertex when we are using static indices
|
|
int nFirstVertex = m_VertexOverride ? 0 : m_nFirstVertex;
|
|
int actualFirstVertex = m_IndexOverride ? nFirstVertex : 0;
|
|
bool bUsingPreTessellatedPatches = ( GetTessellationType() > 0 );
|
|
int nVertexOffsetInBytes = 0;
|
|
int nIDOffsetBytes = 0;
|
|
if ( bUsingPreTessellatedPatches )
|
|
{
|
|
CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( );
|
|
|
|
nVertexOffsetInBytes = ( nFirstVertex + ( nFirstIndex ) ) * g_MeshMgr.VertexFormatSize( GetVertexFormat() );
|
|
nIDOffsetBytes = ( nFirstIndex / 4 ) * pVertexIDBuffer->VertexSize();
|
|
}
|
|
else if ( HasFlexMesh() )
|
|
{
|
|
nVertexOffsetInBytes = nFirstVertex * g_MeshMgr.VertexFormatSize( GetVertexFormat() );
|
|
}
|
|
int baseIndex = m_IndexOverride ? 0 : m_FirstIndex;
|
|
|
|
// Overriding with the dynamic index buffer, preserve state!
|
|
if ( m_IndexOverride && m_pIndexBuffer == g_MeshMgr.GetDynamicIndexBufferInternal() )
|
|
{
|
|
baseIndex = m_FirstIndex;
|
|
}
|
|
|
|
VertexFormat_t fmt = m_VertexOverride ? GetVertexFormat() : VERTEX_FORMAT_INVALID;
|
|
if ( !SetRenderState( nVertexOffsetInBytes, actualFirstVertex, nIDOffsetBytes, fmt ) )
|
|
return;
|
|
|
|
// Draws a portion of the mesh
|
|
int numVertices = m_VertexOverride ? m_pVertexBuffer->VertexCount() : m_TotalVertices;
|
|
if ((nFirstIndex != -1) && (nIndexCount != 0))
|
|
{
|
|
Assert( ( m_Type != MATERIAL_POINTS ) && ( m_Type != MATERIAL_INSTANCED_QUADS ) );
|
|
nFirstIndex += baseIndex;
|
|
}
|
|
else
|
|
{
|
|
// by default we draw the whole thing
|
|
nFirstIndex = baseIndex;
|
|
if( m_IndexOverride )
|
|
{
|
|
nIndexCount = m_pIndexBuffer->IndexCount();
|
|
Assert( ( m_Type != MATERIAL_POINTS ) && ( m_Type != MATERIAL_INSTANCED_QUADS ) );
|
|
Assert( nIndexCount != 0 );
|
|
}
|
|
else
|
|
{
|
|
nIndexCount = m_TotalIndices;
|
|
// Fake out the index count if we're drawing points/instanced-quads
|
|
if ( ( m_Type == MATERIAL_POINTS ) || ( m_Type == MATERIAL_INSTANCED_QUADS ) )
|
|
{
|
|
nIndexCount = numVertices;
|
|
}
|
|
Assert( nIndexCount != 0 );
|
|
}
|
|
}
|
|
|
|
// Fix up nFirstVertex to indicate the first vertex used in the data
|
|
if ( !HasFlexMesh() )
|
|
{
|
|
actualFirstVertex = nFirstVertex - actualFirstVertex;
|
|
}
|
|
|
|
s_FirstVertex = actualFirstVertex;
|
|
s_NumVertices = numVertices;
|
|
|
|
// Build a primlist with 1 element..
|
|
CPrimList prim;
|
|
prim.m_FirstIndex = nFirstIndex;
|
|
prim.m_NumIndices = nIndexCount;
|
|
Assert( nIndexCount != 0 );
|
|
s_pPrims = &prim;
|
|
s_nPrims = 1;
|
|
|
|
DrawMesh( pVecDiffuseModulation );
|
|
|
|
s_pPrims = NULL;
|
|
}
|
|
}
|
|
|
|
void CDynamicMeshDX8::DrawModulated( const Vector4D &vecDiffuseModulation, int nFirstIndex, int nIndexCount )
|
|
{
|
|
DrawInternal( &vecDiffuseModulation, nFirstIndex, nIndexCount );
|
|
}
|
|
|
|
void CDynamicMeshDX8::Draw( int nFirstIndex, int nIndexCount )
|
|
{
|
|
DrawInternal( NULL, nFirstIndex, nIndexCount );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This is useful when we need to dynamically modify data; just set the
|
|
// render state and draw the pass immediately
|
|
//-----------------------------------------------------------------------------
|
|
void CDynamicMeshDX8::DrawSinglePassImmediately()
|
|
{
|
|
if ((m_TotalVertices > 0) || (m_TotalIndices > 0))
|
|
{
|
|
Assert( !m_IsDrawing );
|
|
|
|
// Set the render state
|
|
if ( SetRenderState( 0, 0 ) )
|
|
{
|
|
s_FirstVertex = m_nFirstVertex;
|
|
s_NumVertices = m_TotalVertices;
|
|
|
|
// Make a temporary PrimList to hold the indices.
|
|
CPrimList prim( m_FirstIndex, m_TotalIndices );
|
|
Assert( m_TotalIndices != 0 );
|
|
s_pPrims = &prim;
|
|
s_nPrims = 1;
|
|
|
|
// Render it
|
|
RenderPass( NULL );
|
|
}
|
|
|
|
// We're done with our data
|
|
ResetVertexAndIndexCounts();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// A mesh that stores temporary vertex data in the correct format (for modification)
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// Used in rendering sub-parts of the mesh
|
|
unsigned int CTempMeshDX8::s_NumIndices;
|
|
unsigned int CTempMeshDX8::s_FirstIndex;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CTempMeshDX8::CTempMeshDX8( bool isDynamic ) : m_VertexSize(0xFFFF), m_IsDynamic(isDynamic)
|
|
{
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_Locked = false;
|
|
m_InPass = false;
|
|
#endif
|
|
}
|
|
|
|
CTempMeshDX8::~CTempMeshDX8()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Is the temp mesh dynamic?
|
|
//-----------------------------------------------------------------------------
|
|
bool CTempMeshDX8::IsDynamic() const
|
|
{
|
|
return m_IsDynamic;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the vertex format
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::SetVertexFormat( VertexFormat_t format, bool bHasVertexOverride, bool bHasIndexOverride )
|
|
{
|
|
CBaseMeshDX8::SetVertexFormat(format, bHasVertexOverride, bHasIndexOverride);
|
|
m_VertexSize = g_MeshMgr.VertexFormatSize( format );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// returns the # of vertices (static meshes only)
|
|
//-----------------------------------------------------------------------------
|
|
int CTempMeshDX8::VertexCount() const
|
|
{
|
|
return m_VertexSize ? m_VertexData.Count() / m_VertexSize : 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// returns the # of indices
|
|
//-----------------------------------------------------------------------------
|
|
int CTempMeshDX8::IndexCount( ) const
|
|
{
|
|
return m_IndexData.Count();
|
|
}
|
|
|
|
void CTempMeshDX8::ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
Assert( !m_Locked );
|
|
|
|
m_LockedVerts = nVertexCount;
|
|
m_LockedIndices = nIndexCount;
|
|
|
|
if( nVertexCount > 0 )
|
|
{
|
|
int vertexByteOffset = m_VertexSize * nFirstVertex;
|
|
|
|
// Lock it baby
|
|
unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset];
|
|
|
|
// Compute the vertex index..
|
|
desc.m_nFirstVertex = vertexByteOffset / m_VertexSize;
|
|
|
|
// Set up the mesh descriptor
|
|
g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc );
|
|
}
|
|
else
|
|
{
|
|
desc.m_nFirstVertex = 0;
|
|
// Set up the mesh descriptor
|
|
g_MeshMgr.ComputeVertexDescription( 0, 0, desc );
|
|
}
|
|
|
|
if (m_Type != MATERIAL_POINTS && nIndexCount > 0 )
|
|
{
|
|
desc.m_pIndices = &m_IndexData[nFirstIndex];
|
|
desc.m_nIndexSize = 1;
|
|
}
|
|
else
|
|
{
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
}
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_Locked = true;
|
|
#endif
|
|
}
|
|
|
|
void CTempMeshDX8::ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, desc );
|
|
}
|
|
|
|
void CTempMeshDX8::ModifyEnd( MeshDesc_t& desc )
|
|
{
|
|
#ifdef DBGFLAG_ASSERT
|
|
Assert( m_Locked );
|
|
m_Locked = false;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Locks/unlocks the mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc, MeshBuffersAllocationSettings_t *pSettings )
|
|
{
|
|
ShaderUtil()->SyncMatrices();
|
|
|
|
g_ShaderMutex.Lock();
|
|
|
|
Assert( !m_Locked );
|
|
|
|
m_LockedVerts = nVertexCount;
|
|
m_LockedIndices = nIndexCount;
|
|
|
|
if( nVertexCount > 0 )
|
|
{
|
|
int vertexByteOffset = m_VertexData.AddMultipleToTail( m_VertexSize * nVertexCount );
|
|
|
|
// Lock it baby
|
|
unsigned char* pVertexMemory = &m_VertexData[vertexByteOffset];
|
|
|
|
// Compute the vertex index..
|
|
desc.m_nFirstVertex = vertexByteOffset / m_VertexSize;
|
|
|
|
// Set up the mesh descriptor
|
|
g_MeshMgr.ComputeVertexDescription( pVertexMemory, m_VertexFormat, desc );
|
|
}
|
|
else
|
|
{
|
|
desc.m_nFirstVertex = 0;
|
|
// Set up the mesh descriptor
|
|
g_MeshMgr.ComputeVertexDescription( 0, 0, desc );
|
|
}
|
|
|
|
if (m_Type != MATERIAL_POINTS && nIndexCount > 0 )
|
|
{
|
|
int nFirstIndex = m_IndexData.AddMultipleToTail( nIndexCount );
|
|
desc.m_pIndices = &m_IndexData[nFirstIndex];
|
|
desc.m_nIndexSize = 1;
|
|
}
|
|
else
|
|
{
|
|
desc.m_pIndices = (unsigned short*)( g_pScratchIndexBuffer );
|
|
desc.m_nIndexSize = 0;
|
|
}
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_Locked = true;
|
|
#endif
|
|
|
|
CBaseMeshDX8::m_bMeshLocked = true;
|
|
}
|
|
|
|
void CTempMeshDX8::UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t& desc )
|
|
{
|
|
Assert( m_Locked );
|
|
|
|
// Remove unused vertices and indices
|
|
int verticesToRemove = m_LockedVerts - nVertexCount;
|
|
if( verticesToRemove != 0 )
|
|
{
|
|
m_VertexData.RemoveMultiple( m_VertexData.Count() - verticesToRemove, verticesToRemove );
|
|
}
|
|
|
|
int indicesToRemove = m_LockedIndices - nIndexCount;
|
|
if( indicesToRemove != 0 )
|
|
{
|
|
m_IndexData.RemoveMultiple( m_IndexData.Count() - indicesToRemove, indicesToRemove );
|
|
}
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_Locked = false;
|
|
#endif
|
|
|
|
CBaseMeshDX8::m_bMeshLocked = false;
|
|
|
|
g_ShaderMutex.Unlock();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the primitive type
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::SetPrimitiveType( MaterialPrimitiveType_t type )
|
|
{
|
|
// FIXME: Support MATERIAL_INSTANCED_QUADS for CTempMeshDX8 (X360 only)
|
|
Assert( ( type != MATERIAL_INSTANCED_QUADS ) /* || IsX360() */ );
|
|
m_Type = type;
|
|
}
|
|
|
|
MaterialPrimitiveType_t CTempMeshDX8::GetPrimitiveType( ) const
|
|
{
|
|
return m_Type;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the dynamic mesh
|
|
//-----------------------------------------------------------------------------
|
|
CDynamicMeshDX8* CTempMeshDX8::GetDynamicMesh( )
|
|
{
|
|
return static_cast<CDynamicMeshDX8*>(g_MeshMgr.GetActualDynamicMesh( m_VertexFormat ));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Draws the entire mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::DrawInternal( const Vector4D *pVecDiffuseModulation, int nFirstIndex, int nIndexCount )
|
|
{
|
|
if ( !ShaderUtil()->OnDrawMesh( this, nFirstIndex, nIndexCount ) )
|
|
{
|
|
MarkAsDrawn();
|
|
return;
|
|
}
|
|
|
|
if (m_VertexData.Count() > 0)
|
|
{
|
|
if ( !g_pShaderDeviceDx8->IsDeactivated() )
|
|
{
|
|
#ifdef DRAW_SELECTION
|
|
if (!g_bDrawSelection && !ShaderAPI()->IsInSelectionMode())
|
|
#else
|
|
if (!ShaderAPI()->IsInSelectionMode())
|
|
#endif
|
|
{
|
|
s_FirstIndex = nFirstIndex;
|
|
s_NumIndices = nIndexCount;
|
|
|
|
DrawMesh( pVecDiffuseModulation );
|
|
|
|
// This assertion fails if a BeginPass() call was not matched by
|
|
// a RenderPass() call
|
|
Assert(!m_InPass);
|
|
}
|
|
else
|
|
{
|
|
TestSelection();
|
|
}
|
|
}
|
|
|
|
// Clear out the data if this temp mesh is a dynamic one...
|
|
if (m_IsDynamic)
|
|
{
|
|
m_VertexData.RemoveAll();
|
|
m_IndexData.RemoveAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CTempMeshDX8::Draw( int nFirstIndex, int nIndexCount )
|
|
{
|
|
DrawInternal( NULL, nFirstIndex, nIndexCount );
|
|
}
|
|
|
|
void CTempMeshDX8::DrawModulated( const Vector4D &vecDiffuseModulation, int nFirstIndex, int nIndexCount )
|
|
{
|
|
DrawInternal( &vecDiffuseModulation, nFirstIndex, nIndexCount );
|
|
}
|
|
|
|
|
|
void CTempMeshDX8::CopyToMeshBuilder(
|
|
int iStartVert, // Which vertices to copy.
|
|
int nVerts,
|
|
int iStartIndex, // Which indices to copy.
|
|
int nIndices,
|
|
int indexOffset, // This is added to each index.
|
|
CMeshBuilder &builder )
|
|
{
|
|
int startOffset = iStartVert * m_VertexSize;
|
|
int endOffset = (iStartVert + nVerts) * m_VertexSize;
|
|
Assert( startOffset >= 0 && startOffset <= m_VertexData.Count() );
|
|
Assert( endOffset >= 0 && endOffset <= m_VertexData.Count() && endOffset >= startOffset );
|
|
if ( endOffset > startOffset )
|
|
{
|
|
// FIXME: make this a method of CMeshBuilder (so the 'Position' pointer accessor can be removed)
|
|
// make sure it takes a VertexFormat_t parameter for src/dest match validation
|
|
memcpy( (void*)builder.Position(), &m_VertexData[startOffset], endOffset - startOffset );
|
|
builder.AdvanceVertices( nVerts );
|
|
}
|
|
|
|
for ( int i = 0; i < nIndices; ++i )
|
|
{
|
|
builder.Index( m_IndexData[iStartIndex+i] + indexOffset );
|
|
builder.AdvanceIndex();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Selection mode helper functions
|
|
//-----------------------------------------------------------------------------
|
|
static void ComputeModelToView( D3DXMATRIX& modelToView )
|
|
{
|
|
// Get the modelview matrix...
|
|
D3DXMATRIX world, view;
|
|
ShaderAPI()->GetMatrix( MATERIAL_MODEL, (float*)&world );
|
|
ShaderAPI()->GetMatrix( MATERIAL_VIEW, (float*)&view );
|
|
D3DXMatrixMultiply( &modelToView, &world, &view );
|
|
}
|
|
|
|
static float ComputeCullFactor( )
|
|
{
|
|
D3DCULL cullMode = ShaderAPI()->GetCullMode();
|
|
|
|
float cullFactor;
|
|
switch(cullMode)
|
|
{
|
|
case D3DCULL_CCW:
|
|
cullFactor = -1.0f;
|
|
break;
|
|
|
|
case D3DCULL_CW:
|
|
cullFactor = 1.0f;
|
|
break;
|
|
|
|
default:
|
|
cullFactor = 0.0f;
|
|
break;
|
|
};
|
|
|
|
return cullFactor;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Clip to viewport
|
|
//-----------------------------------------------------------------------------
|
|
static int g_NumClipVerts;
|
|
static D3DXVECTOR3 g_ClipVerts[16];
|
|
|
|
static bool PointInsidePlane( D3DXVECTOR3* pVert, int normalInd, float val, bool nearClip )
|
|
{
|
|
if ((val > 0) || nearClip)
|
|
return (val - (*pVert)[normalInd] >= 0);
|
|
else
|
|
return ((*pVert)[normalInd] - val >= 0);
|
|
}
|
|
|
|
static void IntersectPlane( D3DXVECTOR3* pStart, D3DXVECTOR3* pEnd,
|
|
int normalInd, float val, D3DXVECTOR3* pOutVert )
|
|
{
|
|
D3DXVECTOR3 dir;
|
|
D3DXVec3Subtract( &dir, pEnd, pStart );
|
|
Assert( dir[normalInd] != 0.0f );
|
|
float t = (val - (*pStart)[normalInd]) / dir[normalInd];
|
|
pOutVert->x = pStart->x + dir.x * t;
|
|
pOutVert->y = pStart->y + dir.y * t;
|
|
pOutVert->z = pStart->z + dir.z * t;
|
|
|
|
// Avoid any precision problems.
|
|
(*pOutVert)[normalInd] = val;
|
|
}
|
|
|
|
static int ClipTriangleAgainstPlane( D3DXVECTOR3** ppVert, int nVertexCount,
|
|
D3DXVECTOR3** ppOutVert, int normalInd, float val, bool nearClip = false )
|
|
{
|
|
// Ye Olde Sutherland-Hodgman clipping algorithm
|
|
int numOutVerts = 0;
|
|
D3DXVECTOR3* pStart = ppVert[nVertexCount-1];
|
|
bool startInside = PointInsidePlane( pStart, normalInd, val, nearClip );
|
|
for (int i = 0; i < nVertexCount; ++i)
|
|
{
|
|
D3DXVECTOR3* pEnd = ppVert[i];
|
|
bool endInside = PointInsidePlane( pEnd, normalInd, val, nearClip );
|
|
if (endInside)
|
|
{
|
|
if (!startInside)
|
|
{
|
|
IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] );
|
|
ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++];
|
|
}
|
|
ppOutVert[numOutVerts++] = pEnd;
|
|
}
|
|
else
|
|
{
|
|
if (startInside)
|
|
{
|
|
IntersectPlane( pStart, pEnd, normalInd, val, &g_ClipVerts[g_NumClipVerts] );
|
|
ppOutVert[numOutVerts++] = &g_ClipVerts[g_NumClipVerts++];
|
|
}
|
|
}
|
|
pStart = pEnd;
|
|
startInside = endInside;
|
|
}
|
|
|
|
return numOutVerts;
|
|
}
|
|
|
|
void CTempMeshDX8::ClipTriangle( D3DXVECTOR3** ppVert, float zNear, D3DXMATRIX& projection )
|
|
{
|
|
int i;
|
|
int nVertexCount = 3;
|
|
D3DXVECTOR3* ppClipVert1[10];
|
|
D3DXVECTOR3* ppClipVert2[10];
|
|
|
|
g_NumClipVerts = 0;
|
|
|
|
// Clip against the near plane in view space to prevent negative w.
|
|
// Clip against each plane
|
|
nVertexCount = ClipTriangleAgainstPlane( ppVert, nVertexCount, ppClipVert1, 2, zNear, true );
|
|
if (nVertexCount < 3)
|
|
return;
|
|
|
|
// Sucks that I have to do this, but I have to clip near plane in view space
|
|
// Clipping in projection space is screwy when w < 0
|
|
// Transform the clipped points into projection space
|
|
Assert( g_NumClipVerts <= 2 );
|
|
for (i = 0; i < nVertexCount; ++i)
|
|
{
|
|
if (ppClipVert1[i] == &g_ClipVerts[0])
|
|
{
|
|
D3DXVec3TransformCoord( &g_ClipVerts[0], ppClipVert1[i], &projection );
|
|
}
|
|
else if (ppClipVert1[i] == &g_ClipVerts[1])
|
|
{
|
|
D3DXVec3TransformCoord( &g_ClipVerts[1], ppClipVert1[i], &projection );
|
|
}
|
|
else
|
|
{
|
|
D3DXVec3TransformCoord( &g_ClipVerts[g_NumClipVerts], ppClipVert1[i], &projection );
|
|
ppClipVert1[i] = &g_ClipVerts[g_NumClipVerts];
|
|
++g_NumClipVerts;
|
|
}
|
|
}
|
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 2, 1.0f );
|
|
if (nVertexCount < 3)
|
|
return;
|
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 0, 1.0f );
|
|
if (nVertexCount < 3)
|
|
return;
|
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 0, -1.0f );
|
|
if (nVertexCount < 3)
|
|
return;
|
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert2, nVertexCount, ppClipVert1, 1, 1.0f );
|
|
if (nVertexCount < 3)
|
|
return;
|
|
|
|
nVertexCount = ClipTriangleAgainstPlane( ppClipVert1, nVertexCount, ppClipVert2, 1, -1.0f );
|
|
if (nVertexCount < 3)
|
|
return;
|
|
|
|
#ifdef DRAW_SELECTION
|
|
if( 1 || g_bDrawSelection )
|
|
{
|
|
srand( *(int*)(&ppClipVert2[0]->x) );
|
|
unsigned char r = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64;
|
|
unsigned char g = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64;
|
|
unsigned char b = (unsigned char)(rand() * 191.0f / VALVE_RAND_MAX) + 64;
|
|
|
|
ShaderAPI()->SetupSelectionModeVisualizationState();
|
|
|
|
CMeshBuilder* pMeshBuilder = ShaderAPI()->GetVertexModifyBuilder();
|
|
IMesh* pMesh = GetDynamicMesh();
|
|
pMeshBuilder->Begin( pMesh, MATERIAL_POLYGON, nVertexCount );
|
|
|
|
for ( i = 0; i < nVertexCount; ++i)
|
|
{
|
|
pMeshBuilder->Position3fv( *ppClipVert2[i] );
|
|
pMeshBuilder->Color3ub( r, g, b );
|
|
pMeshBuilder->AdvanceVertex();
|
|
}
|
|
|
|
pMeshBuilder->End();
|
|
pMesh->Draw();
|
|
|
|
pMeshBuilder->Begin( pMesh, MATERIAL_LINE_LOOP, nVertexCount );
|
|
|
|
for ( i = 0; i < nVertexCount; ++i)
|
|
{
|
|
pMeshBuilder->Position3fv( *ppClipVert2[i] );
|
|
pMeshBuilder->Color3ub( 255, 255, 255 );
|
|
pMeshBuilder->AdvanceVertex();
|
|
}
|
|
|
|
pMeshBuilder->End();
|
|
pMesh->Draw();
|
|
}
|
|
#endif
|
|
|
|
// Compute closest and furthest verts
|
|
float minz = ppClipVert2[0]->z;
|
|
float maxz = ppClipVert2[0]->z;
|
|
for ( i = 1; i < nVertexCount; ++i )
|
|
{
|
|
if (ppClipVert2[i]->z < minz)
|
|
minz = ppClipVert2[i]->z;
|
|
else if (ppClipVert2[i]->z > maxz)
|
|
maxz = ppClipVert2[i]->z;
|
|
}
|
|
|
|
ShaderAPI()->RegisterSelectionHit( minz, maxz );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Selection mode
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::TestSelection()
|
|
{
|
|
// Note that this doesn't take into account any vertex modification
|
|
// done in a vertex shader. Also it doesn't take into account any clipping
|
|
// done in hardware
|
|
|
|
// Blow off points and lines; they don't matter
|
|
if ((m_Type != MATERIAL_TRIANGLES) && (m_Type != MATERIAL_TRIANGLE_STRIP))
|
|
return;
|
|
|
|
D3DXMATRIX modelToView, projection;
|
|
ComputeModelToView( modelToView );
|
|
ShaderAPI()->GetMatrix( MATERIAL_PROJECTION, (float*)&projection );
|
|
float zNear = -projection.m[3][2] / projection.m[2][2];
|
|
|
|
D3DXVECTOR3* pPos[3];
|
|
D3DXVECTOR3 edge[2];
|
|
D3DXVECTOR3 normal;
|
|
|
|
int numTriangles;
|
|
if (m_Type == MATERIAL_TRIANGLES)
|
|
numTriangles = m_IndexData.Count() / 3;
|
|
else
|
|
numTriangles = m_IndexData.Count() - 2;
|
|
|
|
float cullFactor = ComputeCullFactor();
|
|
|
|
// Makes the lovely loop simpler
|
|
if (m_Type == MATERIAL_TRIANGLE_STRIP)
|
|
cullFactor *= -1.0f;
|
|
|
|
// We'll need some temporary memory to tell us if we're transformed the vert
|
|
int nVertexCount = m_VertexData.Count() / m_VertexSize;
|
|
static CUtlVector< unsigned char > transformedVert;
|
|
int transformedVertSize = (nVertexCount + 7) >> 3;
|
|
transformedVert.RemoveAll();
|
|
transformedVert.EnsureCapacity( transformedVertSize );
|
|
transformedVert.AddMultipleToTail( transformedVertSize );
|
|
memset( transformedVert.Base(), 0, transformedVertSize );
|
|
|
|
int indexPos;
|
|
for (int i = 0; i < numTriangles; ++i)
|
|
{
|
|
// Get the three indices
|
|
if (m_Type == MATERIAL_TRIANGLES)
|
|
{
|
|
indexPos = i * 3;
|
|
}
|
|
else
|
|
{
|
|
Assert( m_Type == MATERIAL_TRIANGLE_STRIP );
|
|
cullFactor *= -1.0f;
|
|
indexPos = i;
|
|
}
|
|
|
|
// BAH. Gotta clip to the near clip plane in view space to prevent
|
|
// negative w coords; negative coords throw off the projection-space clipper.
|
|
|
|
// Get the three positions in view space
|
|
int inFrontIdx = -1;
|
|
for (int j = 0; j < 3; ++j)
|
|
{
|
|
int index = m_IndexData[indexPos];
|
|
D3DXVECTOR3* pPosition = (D3DXVECTOR3*)&m_VertexData[index * m_VertexSize];
|
|
if ((transformedVert[index >> 3] & (1 << (index & 0x7))) == 0)
|
|
{
|
|
D3DXVec3TransformCoord( pPosition, pPosition, &modelToView );
|
|
transformedVert[index >> 3] |= (1 << (index & 0x7));
|
|
}
|
|
|
|
pPos[j] = pPosition;
|
|
if (pPos[j]->z < 0.0f)
|
|
inFrontIdx = j;
|
|
++indexPos;
|
|
}
|
|
|
|
// all points are behind the camera
|
|
if (inFrontIdx < 0)
|
|
continue;
|
|
|
|
// backface cull....
|
|
D3DXVec3Subtract( &edge[0], pPos[1], pPos[0] );
|
|
D3DXVec3Subtract( &edge[1], pPos[2], pPos[0] );
|
|
D3DXVec3Cross( &normal, &edge[0], &edge[1] );
|
|
float dot = D3DXVec3Dot( &normal, pPos[inFrontIdx] );
|
|
if (dot * cullFactor > 0.0f)
|
|
continue;
|
|
|
|
// Clip to viewport
|
|
ClipTriangle( pPos, zNear, projection );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Begins a render pass
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::BeginPass( )
|
|
{
|
|
Assert( !m_InPass );
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_InPass = true;
|
|
#endif
|
|
|
|
CMeshBuilder* pMeshBuilder = &g_MeshMgr.m_ModifyBuilder;
|
|
|
|
CDynamicMeshDX8* pMesh = GetDynamicMesh( );
|
|
|
|
int nIndexCount;
|
|
int nFirstIndex;
|
|
if ((s_FirstIndex == -1) && (s_NumIndices == 0))
|
|
{
|
|
nIndexCount = m_IndexData.Count();
|
|
nFirstIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
nIndexCount = s_NumIndices;
|
|
nFirstIndex = s_FirstIndex;
|
|
}
|
|
|
|
int i;
|
|
int nVertexCount = m_VertexData.Count() / m_VertexSize;
|
|
pMeshBuilder->Begin( pMesh, m_Type, nVertexCount, nIndexCount );
|
|
|
|
// Copy in the vertex data...
|
|
// Note that since we pad the vertices, it's faster for us to simply
|
|
// copy the fields we're using...
|
|
Assert( pMeshBuilder->BaseVertexData() );
|
|
memcpy( pMeshBuilder->BaseVertexData(), m_VertexData.Base(), m_VertexData.Count() );
|
|
pMeshBuilder->AdvanceVertices( m_VertexData.Count() / m_VertexSize );
|
|
|
|
for ( i = 0; i < nIndexCount; ++i )
|
|
{
|
|
pMeshBuilder->Index( m_IndexData[nFirstIndex+i] );
|
|
pMeshBuilder->AdvanceIndex();
|
|
}
|
|
|
|
// NOTE: The client is expected to modify the data after this call is made
|
|
pMeshBuilder->Reset();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Draws a single pass
|
|
//-----------------------------------------------------------------------------
|
|
void CTempMeshDX8::RenderPass( const unsigned char *pInstanceCommandBuffer )
|
|
{
|
|
Assert( m_InPass );
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
m_InPass = false;
|
|
#endif
|
|
|
|
// Done building the mesh
|
|
g_MeshMgr.m_ModifyBuilder.End();
|
|
|
|
// Have the dynamic mesh render a single pass...
|
|
GetDynamicMesh()->DrawSinglePassImmediately();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Mesh manager implementation
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CMeshMgr::CMeshMgr() :
|
|
m_pDynamicIndexBuffer(0),
|
|
m_DynamicTempMesh(true),
|
|
m_pVertexIDBuffer(0),
|
|
m_pEmptyColorBuffer(0),
|
|
m_pCurrentVertexBuffer( NULL ),
|
|
m_CurrentVertexFormat( 0 ),
|
|
m_pCurrentIndexBuffer( NULL ),
|
|
m_DynamicIndexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, MATERIAL_INDEX_FORMAT_16BIT, INDEX_BUFFER_SIZE, "dynamic" ),
|
|
m_DynamicVertexBuffer( SHADER_BUFFER_TYPE_DYNAMIC, VERTEX_FORMAT_UNKNOWN, DYNAMIC_VERTEX_BUFFER_MEMORY, "dynamic" )
|
|
{
|
|
m_bUseFatVertices = false;
|
|
m_nIndexBufferOffset = 0;
|
|
memset( m_pVertexBufferOffset, 0, sizeof(m_pVertexBufferOffset) );
|
|
memset( m_pCurrentVertexStride, 0, sizeof(m_pCurrentVertexStride) );
|
|
memset( m_pFirstVertex, 0, sizeof(m_pFirstVertex) );
|
|
memset( m_pVertexCount, 0, sizeof(m_pVertexCount) );
|
|
m_nUnusedVertexFields = 0;
|
|
m_nUnusedTextureCoords = 0;
|
|
memset( m_pPreTessPatchIndexBuffer, 0, sizeof(m_pPreTessPatchIndexBuffer) );
|
|
memset( m_pPreTessPatchVertexBuffer, 0, sizeof(m_pPreTessPatchVertexBuffer) );
|
|
m_pZeroVertexBuffer = NULL;
|
|
}
|
|
|
|
CMeshMgr::~CMeshMgr()
|
|
{
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initialize, shutdown
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::Init()
|
|
{
|
|
#ifdef _GAMECONSOLE
|
|
s_nMemoryFrame = 0;
|
|
for ( int i = 0; i < MAX_TEMP_BUFFER; ++i )
|
|
{
|
|
// NOTE: Debugging modes consume a bunch of this. Need only 64 when not using them.
|
|
static int nStackCount = 0;
|
|
CFmtStr stackName( "CMeshMgr::s_BufferMemory[%d]", nStackCount++ );
|
|
s_BufferMemory[i].Init( (const char *)stackName, ( IsPS3() ? 2 /* PS3 allocates more objects */ : 1 ) * 256 * 1024, 32 * 1024, 32 * 1024 );
|
|
}
|
|
#endif
|
|
|
|
m_DynamicMesh.Init( 0 );
|
|
m_DynamicFlexMesh.Init( 1 );
|
|
|
|
// The dynamic index buffer
|
|
m_pDynamicIndexBuffer = new CIndexBuffer( Dx9Device(), INDEX_BUFFER_SIZE, ShaderAPI()->UsingSoftwareVertexProcessing(), true );
|
|
|
|
// If we're running in vs3.0, allocate a vertexID buffer
|
|
CreateVertexIDBuffer();
|
|
CreateZeroVertexBuffer();
|
|
CreateEmptyColorBuffer();
|
|
|
|
// If we're running in vs3.0, allocate index and vertex buffers for pre-tessellated patches
|
|
CreatePreTessPatchIndexBuffers();
|
|
CreatePreTessPatchVertexBuffers();
|
|
|
|
// Track these 2 allocations as well.
|
|
g_VBAllocTracker->TrackMeshAllocations( "CreateDynamicIndexBuffers" );
|
|
m_DynamicIndexBuffer.Allocate();
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
|
|
g_VBAllocTracker->TrackMeshAllocations( "CreateDynamicVertexBuffers" );
|
|
m_DynamicVertexBuffer.Allocate();
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
}
|
|
|
|
void CMeshMgr::Shutdown()
|
|
{
|
|
CleanUp();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Task switch...
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::ReleaseBuffers()
|
|
{
|
|
if ( IsPC() && mat_debugalttab.GetBool() )
|
|
{
|
|
Warning( "mat_debugalttab: CMeshMgr::ReleaseBuffers\n" );
|
|
}
|
|
|
|
CleanUp();
|
|
m_DynamicMesh.Reset( );
|
|
m_DynamicFlexMesh.Reset( );
|
|
}
|
|
|
|
void CMeshMgr::RestoreBuffers()
|
|
{
|
|
if ( IsPC() && mat_debugalttab.GetBool() )
|
|
{
|
|
Warning( "mat_debugalttab: CMeshMgr::RestoreBuffers\n" );
|
|
}
|
|
Init();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Cleans up vertex and index buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::CleanUp()
|
|
{
|
|
if ( m_pDynamicIndexBuffer )
|
|
{
|
|
delete m_pDynamicIndexBuffer;
|
|
m_pDynamicIndexBuffer = 0;
|
|
}
|
|
|
|
DestroyVertexBuffers();
|
|
|
|
// If we're running in vs3.0, destroy a vertexID buffer
|
|
DestroyZeroVertexBuffer();
|
|
DestroyVertexIDBuffer();
|
|
DestroyEmptyColorBuffer();
|
|
DestroyPreTessPatchIndexBuffers();
|
|
DestroyPreTessPatchVertexBuffers();
|
|
|
|
#ifdef _GAMECONSOLE
|
|
m_ExternalMesh.CleanUp();
|
|
m_ExternalFlexMesh.CleanUp();
|
|
// if we need m_ExternalIndexBuffer.CleanUp(), this would be the place to call it
|
|
#endif
|
|
|
|
m_DynamicIndexBuffer.Free();
|
|
m_DynamicVertexBuffer.Free();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Fills a vertexID buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::FillVertexIDBuffer( CVertexBuffer *pVertexIDBuffer, int nCount )
|
|
{
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
// Fill the buffer with the values 0->(nCount-1)
|
|
int nBaseVertexIndex = 0;
|
|
float *pBuffer = (float*)pVertexIDBuffer->Lock( nCount, nBaseVertexIndex );
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
*pBuffer++ = (float)i;
|
|
}
|
|
pVertexIDBuffer->Unlock( nCount );
|
|
}
|
|
|
|
void CMeshMgr::CreateZeroVertexBuffer()
|
|
{
|
|
if ( !m_pZeroVertexBuffer )
|
|
{
|
|
// In GL glVertexAttribPointer() doesn't support strides of 0, so we need to allocate a dummy vertex buffer large enough to handle 16-bit indices with a stride of 4 byte per vertex, plus a bit more for safety (in case basevertexindex is > 0).
|
|
// We could also try just disabling any vertex attribs that fetch from stream 2 and need 0's, but AMD reports this could hit a slow path in the driver. Argh.
|
|
uint nBufSize = IsOpenGL() ? ( 65536 * 2 * 4 ) : 4096;
|
|
HRESULT hr = Dx9Device()->CreateVertexBuffer( nBufSize, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &m_pZeroVertexBuffer, NULL );
|
|
if ( !FAILED( hr ) )
|
|
{
|
|
void *pData = NULL;
|
|
m_pZeroVertexBuffer->Lock( 0, nBufSize, &pData, D3DLOCK_NOSYSLOCK );
|
|
if ( pData )
|
|
{
|
|
V_memset( pData, 0, nBufSize );
|
|
m_pZeroVertexBuffer->Unlock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMeshMgr::DestroyZeroVertexBuffer()
|
|
{
|
|
if ( m_pZeroVertexBuffer )
|
|
{
|
|
m_pZeroVertexBuffer->Release();
|
|
m_pZeroVertexBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates, destroys the vertexID buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::CreateVertexIDBuffer()
|
|
{
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
DestroyVertexIDBuffer();
|
|
|
|
// Track mesh allocations
|
|
g_VBAllocTracker->TrackMeshAllocations( "CreateVertexIDBuffer" );
|
|
if ( g_pHardwareConfig->ActualHasFastVertexTextures() )
|
|
{
|
|
m_pVertexIDBuffer = new CVertexBuffer( Dx9Device(), 0, 0, sizeof(float),
|
|
VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() );
|
|
FillVertexIDBuffer( m_pVertexIDBuffer, VERTEX_BUFFER_SIZE );
|
|
}
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
}
|
|
|
|
void CMeshMgr::DestroyVertexIDBuffer()
|
|
{
|
|
if ( m_pVertexIDBuffer )
|
|
{
|
|
delete m_pVertexIDBuffer;
|
|
m_pVertexIDBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
CVertexBuffer *CMeshMgr::GetVertexIDBuffer( )
|
|
{
|
|
return m_pVertexIDBuffer;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Helper to get the number of vertices to draw with respect to the subdivison
|
|
// amount requested.
|
|
//--------------------------------------------------------------------------------------
|
|
int CMeshMgr::GetNumIndicesForSubdivisionLevel( int iSubdivLevel )
|
|
{
|
|
return ( ( ( iSubdivLevel + 1 ) * 2 + 2 ) * iSubdivLevel ) - 2;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Create an index buffer for a patch of a certain size
|
|
//--------------------------------------------------------------------------------------
|
|
void CMeshMgr::FillPreTessPatchIB( CIndexBuffer* pIndexBuffer, int iSubdivLevel, int nIndexCount )
|
|
{
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
// Fill the buffer with the values 0->(nCount-1)
|
|
int nBaseIndexIndex = 0;
|
|
unsigned short *pIndices = ( unsigned short* )pIndexBuffer->Lock( false, nIndexCount, nBaseIndexIndex );
|
|
if ( !pIndices )
|
|
return;
|
|
|
|
unsigned short iVertIndex = 0;
|
|
UINT iIndex = 0;
|
|
for( int v = 0; v < iSubdivLevel; v++ )
|
|
{
|
|
iVertIndex = ( unsigned short )( ( iSubdivLevel + 1 ) * ( iSubdivLevel - v ) );
|
|
for( int u = 0; u < iSubdivLevel + 1; ++u )
|
|
{
|
|
*pIndices = iVertIndex;
|
|
pIndices ++;
|
|
*pIndices = iVertIndex - ( unsigned short )( iSubdivLevel + 1 );
|
|
pIndices ++;
|
|
iVertIndex++;
|
|
}
|
|
if( v != iSubdivLevel - 1 )
|
|
{
|
|
// add a degenerate tri for stripping
|
|
*pIndices = pIndices[iIndex - 1];
|
|
pIndices ++;
|
|
*pIndices = ( unsigned short )( ( iSubdivLevel + 1 ) * ( iSubdivLevel - ( v + 1 ) ) );
|
|
pIndices ++;
|
|
}
|
|
}
|
|
|
|
pIndexBuffer->Unlock( nIndexCount );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Create an vertex buffer for a patch of a certain size.
|
|
// This patch will contain precalculated UVs and bernstein polynomial coefficients.
|
|
//--------------------------------------------------------------------------------------
|
|
void CMeshMgr::FillPreTessPatchVB( CVertexBuffer* pVertexBuffer, int iSubdivLevel, int nVertexCount )
|
|
{
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
// Fill the buffer with the values 0->(nCount-1)
|
|
int nBaseVertexIndex = 0;
|
|
|
|
PreTessPatchVertex_t *pVertices = ( PreTessPatchVertex_t* )pVertexBuffer->Lock( nVertexCount, nBaseVertexIndex );
|
|
if ( !pVertices )
|
|
return;
|
|
|
|
// Find our step values
|
|
float uDelta = 1.0f / ( float )iSubdivLevel;
|
|
float vDelta = 1.0f / ( float )iSubdivLevel;
|
|
|
|
// Loop through terrain vertices and get height from the heightmap
|
|
float vStart = 0.0f;
|
|
for( int v = 0; v < iSubdivLevel + 1; ++v )
|
|
{
|
|
float uStart = 0.0f;
|
|
for( int u = 0; u < iSubdivLevel + 1; ++u )
|
|
{
|
|
// patch UV parametric coordinates
|
|
pVertices->m_vPatchUV.x = uStart;
|
|
pVertices->m_vPatchUV.y = vStart;
|
|
|
|
// Regular basis functions
|
|
pVertices->m_vBasisU.x = CubicBasis0( uStart );
|
|
pVertices->m_vBasisU.y = CubicBasis1( uStart );
|
|
pVertices->m_vBasisU.z = CubicBasis2( uStart );
|
|
pVertices->m_vBasisU.w = CubicBasis3( uStart );
|
|
|
|
pVertices->m_vBasisV.x = CubicBasis0( vStart );
|
|
pVertices->m_vBasisV.y = CubicBasis1( vStart );
|
|
pVertices->m_vBasisV.z = CubicBasis2( vStart );
|
|
pVertices->m_vBasisV.w = CubicBasis3( vStart );
|
|
|
|
pVertices ++;
|
|
uStart += uDelta;
|
|
}
|
|
vStart += vDelta;
|
|
}
|
|
|
|
pVertexBuffer->Unlock( nVertexCount );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Fills a vertexID buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::FillEmptyColorBuffer( CVertexBuffer *pEmptyColorBuffer, int nCount )
|
|
{
|
|
// Fill the buffer with the values 0->(nCount-1)
|
|
int nBaseVertexIndex = 0;
|
|
D3DCOLOR *pBuffer = (D3DCOLOR*)pEmptyColorBuffer->Lock( nCount, nBaseVertexIndex );
|
|
memset( pBuffer, 0, nCount * sizeof(D3DCOLOR) );
|
|
pEmptyColorBuffer->Unlock( nCount );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates, destroys a fake color mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::CreateEmptyColorBuffer()
|
|
{
|
|
DestroyEmptyColorBuffer();
|
|
|
|
// Track mesh allocations
|
|
g_VBAllocTracker->TrackMeshAllocations( "CreateEmptyColorMesh" );
|
|
m_pEmptyColorBuffer = new CVertexBuffer( Dx9Device(), 0, 0, sizeof(D3DCOLOR),
|
|
VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() );
|
|
FillEmptyColorBuffer( m_pEmptyColorBuffer, VERTEX_BUFFER_SIZE );
|
|
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
}
|
|
|
|
void CMeshMgr::DestroyEmptyColorBuffer()
|
|
{
|
|
if ( m_pEmptyColorBuffer )
|
|
{
|
|
delete m_pEmptyColorBuffer;
|
|
m_pEmptyColorBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
CVertexBuffer *CMeshMgr::GetEmptyColorBuffer( )
|
|
{
|
|
return m_pEmptyColorBuffer;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create pre-tessellated patch index buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::CreatePreTessPatchIndexBuffers()
|
|
{
|
|
// Don't do instanced tessellation on console platforms (360 or ps3)
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
DestroyPreTessPatchIndexBuffers();
|
|
|
|
if ( g_pHardwareConfig->ActualHasFastVertexTextures() )
|
|
{
|
|
for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i )
|
|
{
|
|
int iSubdivLevel = i + 1;
|
|
int nIndexCount = GetNumIndicesForSubdivisionLevel( iSubdivLevel );
|
|
m_pPreTessPatchIndexBuffer[i] = new CIndexBuffer( Dx9Device(), nIndexCount, ShaderAPI()->UsingSoftwareVertexProcessing(), false );
|
|
|
|
FillPreTessPatchIB( m_pPreTessPatchIndexBuffer[i], iSubdivLevel, nIndexCount );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Create pre-tessellated patch vertex buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::CreatePreTessPatchVertexBuffers()
|
|
{
|
|
// Don't do instanced tessellation on console platforms (360 or ps3)
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
DestroyPreTessPatchVertexBuffers();
|
|
|
|
// Track mesh allocations
|
|
g_VBAllocTracker->TrackMeshAllocations( "CreatePreTessPatchVertexBuffers" );
|
|
if ( g_pHardwareConfig->ActualHasFastVertexTextures() )
|
|
{
|
|
for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i )
|
|
{
|
|
int iSubdivLevel = i + 1;
|
|
int nVertexCount = iSubdivLevel + 1;
|
|
nVertexCount *= nVertexCount;
|
|
|
|
m_pPreTessPatchVertexBuffer[i] = new CVertexBuffer( Dx9Device(), 0, 0, sizeof( PreTessPatchVertex_t ),
|
|
nVertexCount, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing() );
|
|
|
|
FillPreTessPatchVB( m_pPreTessPatchVertexBuffer[i], iSubdivLevel, nVertexCount );
|
|
}
|
|
}
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Destroy pre-tessellated patch index / vertex buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::DestroyPreTessPatchIndexBuffers()
|
|
{
|
|
for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i )
|
|
{
|
|
if( m_pPreTessPatchIndexBuffer[i] )
|
|
{
|
|
delete m_pPreTessPatchIndexBuffer[i];
|
|
m_pPreTessPatchIndexBuffer[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
void CMeshMgr::DestroyPreTessPatchVertexBuffers()
|
|
{
|
|
for ( int i = 0; i < MAX_TESS_DIVISIONS_PER_SIDE; ++i )
|
|
{
|
|
if( m_pPreTessPatchVertexBuffer[i] )
|
|
{
|
|
delete m_pPreTessPatchVertexBuffer[i];
|
|
m_pPreTessPatchVertexBuffer[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
CIndexBuffer *CMeshMgr::GetPreTessPatchIndexBuffer( int iSubdivLevel )
|
|
{
|
|
Assert( iSubdivLevel > -1 && iSubdivLevel < MAX_TESS_DIVISIONS_PER_SIDE );
|
|
Assert( m_pPreTessPatchIndexBuffer[ iSubdivLevel ] );
|
|
return m_pPreTessPatchIndexBuffer[ iSubdivLevel ];
|
|
}
|
|
|
|
CVertexBuffer *CMeshMgr::GetPreTessPatchVertexBuffer( int iSubdivLevel )
|
|
{
|
|
Assert( iSubdivLevel > -1 && iSubdivLevel < MAX_TESS_DIVISIONS_PER_SIDE );
|
|
Assert( m_pPreTessPatchVertexBuffer[ iSubdivLevel ] );
|
|
return m_pPreTessPatchVertexBuffer[ iSubdivLevel ];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Unused vertex fields
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::MarkUnusedVertexFields( unsigned int nFlags, int nTexCoordCount, bool *pUnusedTexCoords )
|
|
{
|
|
m_nUnusedVertexFields = nFlags;
|
|
m_nUnusedTextureCoords = 0;
|
|
for ( int i = 0; i < nTexCoordCount; ++i )
|
|
{
|
|
if ( pUnusedTexCoords[i] )
|
|
{
|
|
m_nUnusedTextureCoords |= ( 1 << i );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Allocate temporary arrays either on the stack, or from the heap.
|
|
// Prevents using all the stack when *lots* of objects are rendered to CSM's.
|
|
//-----------------------------------------------------------------------------
|
|
#if defined( CSTRIKE15 ) // 7ls && !defined( _GAMECONSOLE )
|
|
#define STUDIORENDER_TEMP_DATA_MALLOC( typeName, p, n ) const int nTempDataSize##p = (n); void *pvFree##p = NULL; typeName *p = (typeName *) ( ( nTempDataSize##p < 64*1024 ) ? stackalloc( nTempDataSize##p ) : ( pvFree##p = malloc( nTempDataSize##p ) ) );
|
|
#define STUDIORENDER_TEMP_DATA_FREE( p ) free( pvFree##p )
|
|
#else
|
|
#define STUDIORENDER_TEMP_DATA_MALLOC( typeName, p, n ) typeName *p = (typeName *) stackalloc(n);
|
|
#define STUDIORENDER_TEMP_DATA_FREE( p )
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Draws instanced meshes
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::DrawInstances( int nInstanceCount, const MeshInstanceData_t *pInstanceData )
|
|
{
|
|
if ( nInstanceCount == 0 )
|
|
return;
|
|
|
|
// FIXME: Queue?!?
|
|
// if ( !ShaderUtil()->OnDrawMesh( this, pLists, nLists ) )
|
|
// {
|
|
// MarkAsDrawn();
|
|
// return;
|
|
// }
|
|
|
|
// can't do these in selection mode!
|
|
Assert( !ShaderAPI()->IsInSelectionMode() );
|
|
|
|
// NOTE: on the 360/PS3, we can't do too many instances at the same time because
|
|
// we overflow the stack in the compiled state allocation.
|
|
int nBatchSize = IsGameConsole() ? MIN( nInstanceCount, CONSOLE_MAX_MODEL_FAST_PATH_BATCH_SIZE ) : nInstanceCount;
|
|
|
|
// Compute info necessary for the entire render
|
|
const int nCompiledStateSize = nBatchSize * sizeof(CompiledLightingState_t);
|
|
STUDIORENDER_TEMP_DATA_MALLOC( CompiledLightingState_t, pCompiledState, nCompiledStateSize );
|
|
|
|
const int nInfoSize = nBatchSize * sizeof(InstanceInfo_t);
|
|
STUDIORENDER_TEMP_DATA_MALLOC( InstanceInfo_t, pCompiledInfo, nInfoSize );
|
|
|
|
// No flexing allowed if rendering instances
|
|
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
|
|
|
|
VertexCompressionType_t nCompression = CompressionType( pInstanceData[0].m_pVertexBuffer->GetVertexFormat() );
|
|
|
|
#ifdef _DEBUG
|
|
for ( int i = 0; i < nInstanceCount; ++i )
|
|
{
|
|
Assert( nCompression == CompressionType( pInstanceData[i].m_pVertexBuffer->GetVertexFormat() ) );
|
|
}
|
|
#endif
|
|
|
|
g_pInstanceCompiledState = pCompiledState;
|
|
g_pInstanceInfo = pCompiledInfo;
|
|
|
|
while ( nInstanceCount > 0 )
|
|
{
|
|
memset( pCompiledInfo, 0, nBatchSize * sizeof(InstanceInfo_t) );
|
|
|
|
g_nInstanceCount = nBatchSize;
|
|
g_pInstanceData = pInstanceData;
|
|
|
|
// This is going to cause RenderPass to get called a bunch
|
|
ShaderAPI()->DrawMesh( NULL, nBatchSize, pInstanceData, nCompression, pCompiledState, pCompiledInfo );
|
|
|
|
nInstanceCount -= nBatchSize;
|
|
pInstanceData += nBatchSize;
|
|
nBatchSize = MIN( nInstanceCount, CONSOLE_MAX_MODEL_FAST_PATH_BATCH_SIZE );
|
|
}
|
|
|
|
g_pInstanceCompiledState = NULL;
|
|
g_pInstanceInfo = NULL;
|
|
g_nInstanceCount = 0;
|
|
g_pInstanceData = NULL;
|
|
|
|
STUDIORENDER_TEMP_DATA_FREE( pCompiledInfo );
|
|
STUDIORENDER_TEMP_DATA_FREE( pCompiledState );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Is the mesh dynamic?
|
|
//-----------------------------------------------------------------------------
|
|
bool CMeshMgr::IsDynamicMesh( IMesh* pMesh ) const
|
|
{
|
|
return ( pMesh == &m_DynamicMesh ) || ( pMesh == &m_DynamicFlexMesh );
|
|
}
|
|
|
|
bool CMeshMgr::IsDynamicVertexBuffer( IVertexBuffer *pVertexBuffer ) const
|
|
{
|
|
return ( pVertexBuffer == &m_DynamicVertexBuffer );
|
|
}
|
|
|
|
bool CMeshMgr::IsDynamicIndexBuffer( IIndexBuffer *pIndexBuffer ) const
|
|
{
|
|
return ( pIndexBuffer == &m_DynamicIndexBuffer );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Discards the dynamic vertex and index buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::DiscardVertexBuffers()
|
|
{
|
|
VPROF_BUDGET( "CMeshMgr::DiscardVertexBuffers", VPROF_BUDGETGROUP_SWAP_BUFFERS );
|
|
// This shouldn't be necessary, but it seems to be on GeForce 2
|
|
// It helps when running WC and the engine simultaneously.
|
|
ResetMeshRenderState();
|
|
|
|
if ( !g_pShaderDeviceDx8->IsDeactivated() )
|
|
{
|
|
for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; )
|
|
{
|
|
m_DynamicVertexBuffers[i].m_pBuffer->FlushAtFrameStart();
|
|
}
|
|
m_pDynamicIndexBuffer->FlushAtFrameStart();
|
|
}
|
|
|
|
#ifdef _GAMECONSOLE
|
|
// Unbind everything. We're going to be decommitting memory
|
|
// and we don't want the slightest chance that there could be
|
|
// D3D internal state pointing at this memory
|
|
// (defensive fix for tracker bug 49836)
|
|
for ( int i = 0; i < 4; ++i )
|
|
{
|
|
D3DSetStreamSource( i, 0, 0, 0 );
|
|
}
|
|
|
|
g_bUsingVertexID = false;
|
|
g_nLastVertexIDOffset = -1;
|
|
g_bUsingPreTessPatches = false;
|
|
g_pLastStreamSpec = NULL;
|
|
g_pLastVertex = NULL;
|
|
g_pLastVertexBuffer = NULL;
|
|
g_nLastVertOffsetInBytes = -1;
|
|
g_pLastColorBuffer = NULL;
|
|
g_nLastColorMeshVertOffsetInBytes = 0;
|
|
|
|
if ( ++s_nMemoryFrame >= MAX_TEMP_BUFFER )
|
|
{
|
|
s_nMemoryFrame = 0;
|
|
}
|
|
|
|
/*
|
|
static int s_nHisto[ 33 ];
|
|
static int s_nHistoCount;
|
|
int nMem = s_BufferMemory[s_nMemoryFrame].GetUsed() / ( 32 * 1024 );
|
|
s_nHisto[ nMem ]++;
|
|
if ( ( ++s_nHistoCount % 1024 ) == 0 )
|
|
{
|
|
Msg( "DynamicBuffers: " );
|
|
bool bFound = false;
|
|
for( int i = 32; i >= 0; --i )
|
|
{
|
|
if ( s_nHisto[i] )
|
|
{
|
|
bFound = true;
|
|
}
|
|
if ( !bFound )
|
|
continue;
|
|
Msg( "[%dk %d] ", i * 32, s_nHisto[i] );
|
|
}
|
|
Msg( "\n" );
|
|
}
|
|
*/
|
|
s_BufferMemory[s_nMemoryFrame].FreeAll();
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Releases all dynamic vertex buffers
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::DestroyVertexBuffers()
|
|
{
|
|
g_bUsingVertexID = false;
|
|
g_nLastVertexIDOffset = -1;
|
|
g_bUsingPreTessPatches = false;
|
|
g_pLastStreamSpec = NULL;
|
|
g_pLastVertex = NULL;
|
|
g_pLastVertexBuffer = NULL;
|
|
g_nLastVertOffsetInBytes = -1;
|
|
g_pLastColorBuffer = NULL;
|
|
g_nLastColorMeshVertOffsetInBytes = 0;
|
|
|
|
if( !Dx9Device() )
|
|
{
|
|
return; // Dx device wasn't initialized yet, we have nothing to clean up
|
|
}
|
|
|
|
// Necessary for cleanup
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
D3DSetStreamSource( 0, 0, 0, 0 );
|
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 );
|
|
RECORD_INT( 1 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
D3DSetStreamSource( 1, 0, 0, 0 );
|
|
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 );
|
|
RECORD_INT( 2 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
D3DSetStreamSource( 2, 0, 0, 0 );
|
|
|
|
#ifndef _X360
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 );
|
|
RECORD_INT( 3 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( 0 );
|
|
D3DSetStreamSource( 3, 0, 0, 0 );
|
|
#endif
|
|
|
|
for (int i = m_DynamicVertexBuffers.Count(); --i >= 0; )
|
|
{
|
|
if (m_DynamicVertexBuffers[i].m_pBuffer)
|
|
{
|
|
delete m_DynamicVertexBuffers[i].m_pBuffer;
|
|
}
|
|
}
|
|
m_DynamicVertexBuffers.RemoveAll();
|
|
m_DynamicMesh.Reset();
|
|
m_DynamicFlexMesh.Reset();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates, destroys static meshes
|
|
//-----------------------------------------------------------------------------
|
|
IMesh* CMeshMgr::CreateStaticMesh( VertexFormat_t format, const char *pTextureBudgetGroup, IMaterial * pMaterial, VertexStreamSpec_t *pStreamSpec )
|
|
{
|
|
// FIXME: Use a fixed-size allocator
|
|
CMeshDX8* pNewMesh = new CMeshDX8( pTextureBudgetGroup );
|
|
pNewMesh->SetVertexStreamSpec( pStreamSpec );
|
|
pNewMesh->SetVertexFormat( format, false, false );
|
|
if ( pMaterial != NULL )
|
|
{
|
|
pNewMesh->SetMaterial( pMaterial );
|
|
}
|
|
return pNewMesh;
|
|
}
|
|
|
|
void CMeshMgr::DestroyStaticMesh( IMesh* pMesh )
|
|
{
|
|
// Don't destroy the dynamic mesh!
|
|
Assert( !IsDynamicMesh( pMesh ) );
|
|
CBaseMeshDX8* pMeshImp = static_cast<CBaseMeshDX8*>(pMesh);
|
|
if (pMeshImp)
|
|
{
|
|
delete pMeshImp;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets at the *real* dynamic mesh
|
|
//-----------------------------------------------------------------------------
|
|
IMesh* CMeshMgr::GetActualDynamicMesh( VertexFormat_t format )
|
|
{
|
|
m_DynamicMesh.SetVertexFormat( format, false, false );
|
|
return &m_DynamicMesh;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copy a static mesh index buffer to a dynamic mesh index buffer
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::CopyStaticMeshIndexBufferToTempMeshIndexBuffer( CTempMeshDX8 *pDstIndexMesh,
|
|
CMeshDX8 *pSrcIndexMesh )
|
|
{
|
|
Assert( !pSrcIndexMesh->IsDynamic() );
|
|
int nIndexCount = pSrcIndexMesh->IndexCount();
|
|
|
|
CMeshBuilder dstMeshBuilder;
|
|
dstMeshBuilder.Begin( pDstIndexMesh, pSrcIndexMesh->GetPrimitiveType(), 0, nIndexCount );
|
|
CIndexBuffer *srcIndexBuffer = pSrcIndexMesh->GetIndexBuffer();
|
|
int dummy = 0;
|
|
unsigned short *srcIndexArray = srcIndexBuffer->Lock( false, nIndexCount, dummy, 0 );
|
|
int i;
|
|
for( i = 0; i < nIndexCount; i++ )
|
|
{
|
|
dstMeshBuilder.Index( srcIndexArray[i] );
|
|
dstMeshBuilder.AdvanceIndex();
|
|
}
|
|
srcIndexBuffer->Unlock( 0 );
|
|
dstMeshBuilder.End();
|
|
}
|
|
|
|
|
|
IMesh *CMeshMgr::GetFlexMesh()
|
|
{
|
|
if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 92 )
|
|
{
|
|
// FIXME: Kinda ugly size.. 28 bytes
|
|
m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_WRINKLE | VERTEX_FORMAT_USE_EXACT_FORMAT, false, false );
|
|
}
|
|
else
|
|
{
|
|
// Same size as a pair of float3s (24 bytes)
|
|
m_DynamicFlexMesh.SetVertexFormat( VERTEX_POSITION | VERTEX_NORMAL | VERTEX_FORMAT_USE_EXACT_FORMAT, false, false );
|
|
}
|
|
return &m_DynamicFlexMesh;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets at the dynamic mesh
|
|
//-----------------------------------------------------------------------------
|
|
IMesh* CMeshMgr::GetDynamicMesh( IMaterial* pMaterial, VertexFormat_t vertexFormat, int nHWSkinBoneCount,
|
|
bool buffered, IMesh* pVertexOverride, IMesh* pIndexOverride )
|
|
{
|
|
Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );
|
|
|
|
IMaterialInternal* pMatInternal = static_cast<IMaterialInternal*>(pMaterial);
|
|
|
|
bool needTempMesh = ShaderAPI()->IsInSelectionMode();
|
|
|
|
#ifdef DRAW_SELECTION
|
|
if( g_bDrawSelection )
|
|
{
|
|
needTempMesh = true;
|
|
}
|
|
#endif
|
|
|
|
CBaseMeshDX8* pMesh;
|
|
|
|
if ( needTempMesh )
|
|
{
|
|
// These haven't been implemented yet for temp meshes!
|
|
// I'm not a hundred percent sure how to implement them; it would
|
|
// involve a lock and a copy at least, which would stall the entire
|
|
// rendering pipeline.
|
|
Assert( !pVertexOverride );
|
|
|
|
if( pIndexOverride )
|
|
{
|
|
CopyStaticMeshIndexBufferToTempMeshIndexBuffer( &m_DynamicTempMesh,
|
|
( CMeshDX8 * )pIndexOverride );
|
|
}
|
|
pMesh = &m_DynamicTempMesh;
|
|
}
|
|
else
|
|
{
|
|
pMesh = &m_DynamicMesh;
|
|
}
|
|
|
|
// HACK: SetVertexFormat here will slam both the vertex + index buffer
|
|
// to use the default. Some patterns actually do the insane thing of
|
|
// passing the dynamic mesh as its own override (BindBatch, for example).
|
|
// Cache off the override buffers before this happens (in SetVertexFormat of all things)
|
|
CBaseMeshDX8* pBaseVertexOverride = static_cast<CBaseMeshDX8*>( pVertexOverride );
|
|
CBaseMeshDX8* pBaseIndexOverride = static_cast<CBaseMeshDX8*>( pIndexOverride );
|
|
CVertexBuffer *pVertexOverrideBuffer = ( pBaseVertexOverride ) ? pBaseVertexOverride->GetVertexBuffer() : NULL;
|
|
CIndexBuffer *pIndexOverrideBuffer = ( pBaseIndexOverride ) ? pBaseIndexOverride->GetIndexBuffer() : NULL;
|
|
|
|
if( !pBaseVertexOverride )
|
|
{
|
|
// Remove VERTEX_FORMAT_COMPRESSED from the material's format (dynamic meshes don't
|
|
// support compression, and all materials should support uncompressed verts too)
|
|
VertexFormat_t materialFormat = pMatInternal->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED;
|
|
VertexFormat_t fmt = ( vertexFormat != 0 ) ? vertexFormat : materialFormat;
|
|
if ( vertexFormat != 0 )
|
|
{
|
|
int nVertexFormatBoneWeights = NumBoneWeights( vertexFormat );
|
|
if ( nHWSkinBoneCount < nVertexFormatBoneWeights )
|
|
{
|
|
nHWSkinBoneCount = nVertexFormatBoneWeights;
|
|
}
|
|
}
|
|
|
|
// Force the requested number of bone weights
|
|
fmt &= ~VERTEX_BONE_WEIGHT_MASK;
|
|
if ( nHWSkinBoneCount > 0 )
|
|
{
|
|
fmt |= VERTEX_BONEWEIGHT( 2 );
|
|
fmt |= VERTEX_BONE_INDEX;
|
|
}
|
|
|
|
pMesh->SetVertexFormat( fmt, false, ( pIndexOverrideBuffer != NULL ) );
|
|
}
|
|
else
|
|
{
|
|
pMesh->SetVertexFormat( pBaseVertexOverride->GetVertexFormat(), true, ( pIndexOverrideBuffer != NULL ) );
|
|
}
|
|
pMesh->SetMaterial( pMatInternal );
|
|
|
|
// Note this works because we're guaranteed to not be using a buffered mesh
|
|
// when we have overrides on
|
|
// FIXME: Make work for temp meshes
|
|
if ( pMesh == &m_DynamicMesh )
|
|
{
|
|
if ( pVertexOverrideBuffer )
|
|
{
|
|
m_DynamicMesh.OverrideVertexBuffer( pVertexOverrideBuffer );
|
|
}
|
|
if ( pIndexOverrideBuffer )
|
|
{
|
|
m_DynamicMesh.OverrideIndexBuffer( pIndexOverrideBuffer );
|
|
}
|
|
}
|
|
|
|
return pMesh;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Used to construct vertex data
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::ComputeVertexDescription( unsigned char* pBuffer,
|
|
VertexFormat_t vertexFormat, MeshDesc_t& desc ) const
|
|
{
|
|
ComputeVertexDesc< false >( pBuffer, vertexFormat, (VertexDesc_t &)desc );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the vertex format
|
|
//-----------------------------------------------------------------------------
|
|
VertexFormat_t CMeshMgr::ComputeVertexFormat( unsigned int flags,
|
|
int nTexCoordArraySize, int* pTexCoordDimensions, int numBoneWeights,
|
|
int userDataSize ) const
|
|
{
|
|
// Construct a bitfield that makes sense and is unique from the standard FVF formats
|
|
VertexFormat_t fmt = flags & ~VERTEX_FORMAT_USE_EXACT_FORMAT;
|
|
|
|
if ( g_pHardwareConfig->SupportsCompressedVertices() == VERTEX_COMPRESSION_NONE )
|
|
{
|
|
// Vertex compression is disabled - make sure all materials
|
|
// say "No!" to compressed verts ( tested in IsValidVertexFormat() )
|
|
fmt &= ~VERTEX_FORMAT_COMPRESSED;
|
|
}
|
|
|
|
// This'll take 3 bits at most
|
|
Assert( numBoneWeights <= 4 );
|
|
|
|
if ( numBoneWeights > 0 )
|
|
{
|
|
fmt |= VERTEX_BONEWEIGHT( 2 ); // Always exactly two weights
|
|
}
|
|
|
|
// Size is measured in # of floats
|
|
Assert( userDataSize <= 4 );
|
|
fmt |= VERTEX_USERDATA_SIZE(userDataSize);
|
|
|
|
// NOTE: If pTexCoordDimensions isn't specified, then nTexCoordArraySize
|
|
// is interpreted as meaning that we have n 2D texcoords in the first N texcoord slots
|
|
nTexCoordArraySize = MIN( nTexCoordArraySize, VERTEX_MAX_TEXTURE_COORDINATES );
|
|
for ( int i = 0; i < nTexCoordArraySize; ++i )
|
|
{
|
|
if ( pTexCoordDimensions )
|
|
{
|
|
Assert( pTexCoordDimensions[i] >= 0 && pTexCoordDimensions[i] <= 4 );
|
|
fmt |= VERTEX_TEXCOORD_SIZE( i, pTexCoordDimensions[i] );
|
|
}
|
|
else
|
|
{
|
|
fmt |= VERTEX_TEXCOORD_SIZE( i, 2 );
|
|
}
|
|
}
|
|
|
|
return fmt;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Use fat vertices (for tools)
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::UseFatVertices( bool bUseFat )
|
|
{
|
|
m_bUseFatVertices = bUseFat;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the number of vertices we can render using the dynamic mesh
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::GetMaxToRender( IMesh *pMesh, bool bMaxUntilFlush, int *pMaxVerts, int *pMaxIndices )
|
|
{
|
|
CBaseMeshDX8 *pBaseMesh = static_cast<CBaseMeshDX8*>( pMesh );
|
|
if ( !pBaseMesh )
|
|
{
|
|
*pMaxVerts = 0;
|
|
*pMaxIndices = m_pDynamicIndexBuffer->IndexCount();
|
|
return;
|
|
}
|
|
|
|
// Static mesh? Max you can use is 65535
|
|
if ( !IsDynamicMesh( pMesh ) )
|
|
{
|
|
*pMaxVerts = 65535;
|
|
*pMaxIndices = 65535;
|
|
return;
|
|
}
|
|
|
|
CVertexBuffer *pVertexBuffer = pBaseMesh->GetVertexBuffer();
|
|
CIndexBuffer *pIndexBuffer = pBaseMesh->GetIndexBuffer();
|
|
|
|
if ( !pVertexBuffer )
|
|
{
|
|
*pMaxVerts = 0;
|
|
*pMaxIndices = 0;
|
|
return;
|
|
}
|
|
|
|
if ( !bMaxUntilFlush )
|
|
{
|
|
*pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize();
|
|
if ( *pMaxVerts > 65535 )
|
|
{
|
|
*pMaxVerts = 65535;
|
|
}
|
|
*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0;
|
|
return;
|
|
}
|
|
|
|
*pMaxVerts = pVertexBuffer->NumVerticesUntilFlush();
|
|
*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() - pIndexBuffer->IndexPosition() : 0;
|
|
if ( *pMaxVerts == 0 )
|
|
{
|
|
*pMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / pVertexBuffer->VertexSize();
|
|
}
|
|
if ( *pMaxVerts > 65535 )
|
|
{
|
|
*pMaxVerts = 65535;
|
|
}
|
|
if ( *pMaxIndices == 0 )
|
|
{
|
|
*pMaxIndices = pIndexBuffer ? pIndexBuffer->IndexCount() : 0;
|
|
}
|
|
}
|
|
|
|
int CMeshMgr::GetMaxVerticesToRender( IMaterial *pMaterial )
|
|
{
|
|
Assert( (pMaterial == NULL) || ((IMaterialInternal *)pMaterial)->IsRealTimeVersion() );
|
|
// Be conservative, assume no compression (in here, we don't know if the caller will used a compressed VB or not)
|
|
// FIXME: allow the caller to specify which compression type should be used to compute size from the vertex format
|
|
// (this can vary between multiple VBs/Meshes using the same material)
|
|
VertexFormat_t fmt = pMaterial->GetVertexFormat() & ~VERTEX_FORMAT_COMPRESSED;
|
|
if ( fmt == 0 )
|
|
{
|
|
Warning( "bad vertex size for material %s\n", pMaterial->GetName() );
|
|
return 65535;
|
|
}
|
|
|
|
int nMaxVerts = ShaderAPI()->GetCurrentDynamicVBSize() / VertexFormatSize( fmt );
|
|
if ( nMaxVerts > 65535 )
|
|
{
|
|
nMaxVerts = 65535;
|
|
}
|
|
return nMaxVerts;
|
|
}
|
|
|
|
int CMeshMgr::GetMaxIndicesToRender( )
|
|
{
|
|
return INDEX_BUFFER_SIZE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns a vertex buffer appropriate for the flags
|
|
//-----------------------------------------------------------------------------
|
|
CVertexBuffer *CMeshMgr::FindOrCreateVertexBuffer( int nDynamicBufferId, VertexFormat_t vertexFormat )
|
|
{
|
|
int vertexSize = VertexFormatSize( vertexFormat );
|
|
|
|
while ( m_DynamicVertexBuffers.Count() <= nDynamicBufferId )
|
|
{
|
|
// Track VB allocations (override any prior allocator string set higher up on the callstack)
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
g_VBAllocTracker->TrackMeshAllocations( "CMeshMgr::FindOrCreateVertexBuffer (dynamic VB)" );
|
|
|
|
// create the single 1MB dynamic vb that will be shared amongst all consumers
|
|
// the correct thing is to use the largest expected vertex format size of max elements, but this
|
|
// creates an undesirably large buffer - instead create the buffer we want, and fix consumers that bork
|
|
// NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions
|
|
int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize();
|
|
int nIndex = m_DynamicVertexBuffers.AddToTail();
|
|
m_DynamicVertexBuffers[nIndex].m_VertexSize = 0;
|
|
m_DynamicVertexBuffers[nIndex].m_pBuffer = new CVertexBuffer( Dx9Device(), 0, 0,
|
|
nBufferMemory / VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE, TEXTURE_GROUP_STATIC_VERTEX_BUFFER_OTHER, ShaderAPI()->UsingSoftwareVertexProcessing(), true );
|
|
if ( !m_DynamicVertexBuffers[nIndex].m_pBuffer )
|
|
{
|
|
MemOutOfMemory( sizeof(CVertexBuffer) );
|
|
}
|
|
g_VBAllocTracker->TrackMeshAllocations( NULL );
|
|
}
|
|
|
|
if ( m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize != vertexSize )
|
|
{
|
|
// provide caller with dynamic vb in expected format
|
|
// NOTE: GetCurrentDynamicVBSize returns a smaller value during level transitions
|
|
int nBufferMemory = ShaderAPI()->GetCurrentDynamicVBSize();
|
|
m_DynamicVertexBuffers[nDynamicBufferId].m_VertexSize = vertexSize;
|
|
m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer->ChangeConfiguration( vertexSize, nBufferMemory );
|
|
|
|
// size changed means stream stride needs update
|
|
// mark cached stream state as invalid to reset stream
|
|
if ( nDynamicBufferId == 0 )
|
|
{
|
|
g_pLastVertex = NULL;
|
|
}
|
|
}
|
|
|
|
return m_DynamicVertexBuffers[nDynamicBufferId].m_pBuffer;
|
|
}
|
|
|
|
CIndexBuffer *CMeshMgr::GetDynamicIndexBufferInternal()
|
|
{
|
|
return m_pDynamicIndexBuffer;
|
|
}
|
|
|
|
#ifdef _GAMECONSOLE
|
|
int CMeshMgr::GetDynamicIndexBufferAllocationCount()
|
|
{
|
|
if ( !GetDynamicIndexBufferInternal() )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return GetDynamicIndexBufferInternal()->AllocationCount();
|
|
}
|
|
|
|
|
|
int CMeshMgr::GetDynamicIndexBufferIndicesLeft()
|
|
{
|
|
if ( !GetDynamicIndexBufferInternal() )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return GetDynamicIndexBufferInternal()->GetIndicesLeft();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Backdoor used by the queued context to directly use write-combined memory
|
|
//-----------------------------------------------------------------------------
|
|
IMesh *CMeshMgr::GetExternalMesh( const ExternalMeshInfo_t& info )
|
|
{
|
|
if ( info.m_bFlexMesh )
|
|
{
|
|
m_ExternalFlexMesh.Init( info );
|
|
return &m_ExternalFlexMesh;
|
|
}
|
|
|
|
m_ExternalMesh.Init( info );
|
|
return &m_ExternalMesh;
|
|
}
|
|
|
|
void CMeshMgr::SetExternalMeshData( IMesh *pMesh, const ExternalMeshData_t &data )
|
|
{
|
|
CExternalMeshDX8 *pExternalMesh = assert_cast< CExternalMeshDX8* >( pMesh );
|
|
pExternalMesh->SetExternalData( data );
|
|
}
|
|
|
|
IIndexBuffer *CMeshMgr::GetExternalIndexBuffer( int nIndexCount, uint16 *pIndexData )
|
|
{
|
|
m_ExternalIndexBuffer.Init( nIndexCount, pIndexData );
|
|
return &m_ExternalIndexBuffer;
|
|
}
|
|
|
|
#endif // _GAMECONSOLE
|
|
|
|
IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( IMaterial *pMaterial, bool buffered )
|
|
{
|
|
Assert( 0 );
|
|
return NULL;
|
|
// return ( IMeshDX8 * )GetDynamicMesh( pMaterial, buffered, NULL, NULL );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
IVertexBuffer *CMeshMgr::CreateVertexBuffer( ShaderBufferType_t type, VertexFormat_t fmt, int nVertexCount, const char *pBudgetGroup )
|
|
{
|
|
// FIXME: Use a fixed-size allocator
|
|
CVertexBufferDx8 *pNewVertexBuffer = new CVertexBufferDx8( type, fmt, nVertexCount, pBudgetGroup );
|
|
if ( !pNewVertexBuffer )
|
|
{
|
|
MemOutOfMemory( sizeof(CVertexBuffer) );
|
|
}
|
|
return pNewVertexBuffer;
|
|
}
|
|
|
|
IIndexBuffer *CMeshMgr::CreateIndexBuffer( ShaderBufferType_t bufferType, MaterialIndexFormat_t fmt, int nIndexCount, const char *pBudgetGroup )
|
|
{
|
|
switch( bufferType )
|
|
{
|
|
case SHADER_BUFFER_TYPE_STATIC:
|
|
case SHADER_BUFFER_TYPE_DYNAMIC:
|
|
{
|
|
CIndexBufferDx8 *pIndexBuffer = new CIndexBufferDx8( bufferType, fmt, nIndexCount, pBudgetGroup );
|
|
return pIndexBuffer;
|
|
}
|
|
case SHADER_BUFFER_TYPE_STATIC_TEMP:
|
|
case SHADER_BUFFER_TYPE_DYNAMIC_TEMP:
|
|
Assert( 0 );
|
|
return NULL;
|
|
default:
|
|
Assert( 0 );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void CMeshMgr::DestroyVertexBuffer( IVertexBuffer *pVertexBuffer )
|
|
{
|
|
if ( pVertexBuffer && !IsDynamicVertexBuffer( pVertexBuffer ) )
|
|
{
|
|
delete pVertexBuffer;
|
|
}
|
|
}
|
|
|
|
void CMeshMgr::DestroyIndexBuffer( IIndexBuffer *pIndexBuffer )
|
|
{
|
|
if ( pIndexBuffer && !IsDynamicIndexBuffer( pIndexBuffer ) )
|
|
{
|
|
delete pIndexBuffer;
|
|
}
|
|
}
|
|
|
|
// Do we need to specify the stream here in the case of locking multiple dynamic VBs on different streams?
|
|
IVertexBuffer *CMeshMgr::GetDynamicVertexBuffer( int streamID, VertexFormat_t vertexFormat, bool bBuffered )
|
|
{
|
|
if ( CompressionType( vertexFormat ) != VERTEX_COMPRESSION_NONE )
|
|
{
|
|
// UNDONE: support compressed dynamic meshes if needed (pro: less VB memory, con: time spent compressing)
|
|
DebuggerBreak();
|
|
return NULL;
|
|
}
|
|
|
|
bool needTempMesh = ShaderAPI()->IsInSelectionMode();
|
|
|
|
#ifdef DRAW_SELECTION
|
|
if( g_bDrawSelection )
|
|
{
|
|
needTempMesh = true;
|
|
}
|
|
#endif
|
|
|
|
Assert( !needTempMesh ); // MESHFIXME: don't support temp meshes here yet.
|
|
|
|
CVertexBufferDx8 *pVertexBuffer;
|
|
|
|
if ( needTempMesh )
|
|
{
|
|
Assert( 0 ); // MESHFIXME: don't do this yet.
|
|
// pVertexBuffer = &m_DynamicTempVertexBuffer;
|
|
pVertexBuffer = NULL;
|
|
}
|
|
else
|
|
{
|
|
pVertexBuffer = &m_DynamicVertexBuffer;
|
|
}
|
|
|
|
return pVertexBuffer;
|
|
}
|
|
|
|
IIndexBuffer *CMeshMgr::GetDynamicIndexBuffer()
|
|
{
|
|
#ifdef DBGFLAG_ASSERT
|
|
bool needTempMesh =
|
|
#endif
|
|
ShaderAPI()->IsInSelectionMode();
|
|
|
|
#ifdef DRAW_SELECTION
|
|
if( g_bDrawSelection )
|
|
{
|
|
needTempMesh = true;
|
|
}
|
|
#endif
|
|
|
|
Assert( !needTempMesh ); // don't handle this yet. MESHFIXME
|
|
return &m_DynamicIndexBuffer;
|
|
}
|
|
|
|
void CMeshMgr::SetVertexIDStreamState( int nIDOffsetBytes )
|
|
{
|
|
if ( IsGameConsole() )
|
|
return;
|
|
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
|
|
bool bUsingVertexID = false;//IsUsingVertexID();
|
|
// if ( bUsingVertexID != g_bUsingVertexID )
|
|
{
|
|
if ( bUsingVertexID )
|
|
{
|
|
// NOTE: Morphing doesn't work with dynamic buffers!!! BLEAH
|
|
// It's because the indices (which are not 0 based for dynamic buffers)
|
|
// are accessing both the vertexID buffer + the regular vertex buffer.
|
|
// This *might* be fixable with baseVertexIndex?
|
|
Assert( !m_pCurrentVertexBuffer->IsDynamic() );
|
|
|
|
CVertexBuffer *pVertexIDBuffer = g_MeshMgr.GetVertexIDBuffer( );
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( pVertexIDBuffer->UID() );
|
|
RECORD_INT( 3 );
|
|
RECORD_INT( 0 );
|
|
RECORD_INT( pVertexIDBuffer->VertexSize() );
|
|
|
|
D3DSetStreamSource( 3, pVertexIDBuffer->GetInterface(), 0, pVertexIDBuffer->VertexSize() );
|
|
pVertexIDBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
}
|
|
else
|
|
{
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 ); // vertex buffer id
|
|
RECORD_INT( 3 ); // stream
|
|
RECORD_INT( 0 ); // vertex offset
|
|
RECORD_INT( 0 ); // vertex size
|
|
|
|
D3DSetStreamSource( 3, 0, 0, 0 );
|
|
}
|
|
g_bUsingVertexID = bUsingVertexID;
|
|
g_nLastVertexIDOffset = nIDOffsetBytes;
|
|
}
|
|
}
|
|
|
|
void CMeshMgr::SetTessellationStreamState( int nVertOffsetInBytes, int iSubdivLevel )
|
|
{
|
|
// empty for now
|
|
}
|
|
|
|
void CMeshMgr::SetCustomStreamsState()
|
|
{
|
|
if ( g_pLastStreamSpec )
|
|
{
|
|
for ( int k = 0; k < ARRAYSIZE( g_bCustomStreamsSet ); ++ k )
|
|
{
|
|
if ( g_bCustomStreamsSet[k] )
|
|
{
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 ); // vertex buffer id
|
|
RECORD_INT( k ); // stream
|
|
RECORD_INT( 0 ); // vertex offset
|
|
RECORD_INT( 0 ); // vertex size
|
|
|
|
D3DSetStreamSource( k, 0, 0, 0 );
|
|
|
|
g_bCustomStreamsSet[k] = false;
|
|
}
|
|
}
|
|
}
|
|
g_pLastStreamSpec = NULL;
|
|
}
|
|
|
|
void CMeshMgr::SetColorStreamState()
|
|
{
|
|
if ( g_pLastColorBuffer )
|
|
{
|
|
RECORD_COMMAND( DX8_SET_STREAM_SOURCE, 4 );
|
|
RECORD_INT( -1 ); // vertex buffer id
|
|
RECORD_INT( 1 ); // stream
|
|
RECORD_INT( 0 ); // vertex offset
|
|
RECORD_INT( 0 ); // vertex size
|
|
|
|
D3DSetStreamSource( 1, 0, 0, 0 );
|
|
}
|
|
g_pLastColorBuffer = NULL;
|
|
g_nLastColorMeshVertOffsetInBytes = 0;
|
|
}
|
|
|
|
void CMeshMgr::SetVertexStreamState( int nVertOffsetInBytes, int nVertexStride )
|
|
{
|
|
// Calls in here assume shader support...
|
|
// HACK...point stream 2 at the same VB which is bound to stream 0...
|
|
//Assert( m_pCurrentVertexBuffer && m_pCurrentVertexBuffer->GetDx9Buffer() );
|
|
//D3DSetStreamSource( 2, m_pCurrentVertexBuffer->GetDx9Buffer(), nVertOffsetInBytes, nVertexStride );
|
|
|
|
// Set a 4kb all-zero static VB into the flex/wrinkle stream with a stride of 0 bytes, so the vertex shader always reads valid floating point values (otherwise it can get NaN's/Inf's, and under OpenGL this is bad on NVidia)
|
|
// togl requires non-zero strides, but on D3D9 we can set a stride of 0 for a little more efficiency.
|
|
D3DSetStreamSource( 2, g_MeshMgr.GetZeroVertexBuffer(), 0, IsOpenGL() ? 4 : 0 );
|
|
|
|
// cFlexScale.x masks flex in vertex shader
|
|
float c[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
ShaderAPI()->SetVertexShaderConstant( VERTEX_SHADER_FLEXSCALE, c, 1 );
|
|
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
|
|
if ( g_pLastVertex || ( g_pLastVertexBuffer != m_pCurrentVertexBuffer->GetDx9Buffer() ) ||
|
|
( g_nLastVertOffsetInBytes != nVertOffsetInBytes ) || ( g_nLastVertStride != nVertexStride ))
|
|
{
|
|
Assert( m_pCurrentVertexBuffer && m_pCurrentVertexBuffer->GetDx9Buffer() );
|
|
|
|
D3DSetStreamSource( 0, m_pCurrentVertexBuffer->GetDx9Buffer(), nVertOffsetInBytes, nVertexStride );
|
|
m_pCurrentVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastVertex = NULL;
|
|
g_nLastVertStride = nVertexStride;
|
|
g_pLastVertexBuffer = m_pCurrentVertexBuffer->GetDx9Buffer();
|
|
g_nLastVertOffsetInBytes = nVertOffsetInBytes;
|
|
}
|
|
}
|
|
|
|
bool CMeshMgr::SetRenderState( int nVertexOffsetInBytes, int nFirstVertexIdx, VertexFormat_t vertexFormat, int nVertexStride )
|
|
{
|
|
// Can't set the state if we're deactivated
|
|
if ( g_pShaderDeviceDx8->IsDeactivated() )
|
|
{
|
|
ResetMeshRenderState();
|
|
return false;
|
|
}
|
|
|
|
// make sure the vertex format is a superset of the current material's
|
|
// vertex format...
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
|
|
#if 0
|
|
// FIXME
|
|
if ( !IsValidVertexFormat( vertexFormat ) )
|
|
{
|
|
Warning( "Material %s is being applied to a model, you need $model=1 in the .vmt file!\n",
|
|
ShaderAPI()->GetBoundMaterial()->GetName() );
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
SetVertexIDStreamState( 0 );
|
|
SetColorStreamState();
|
|
SetCustomStreamsState();
|
|
SetVertexStreamState( nVertexOffsetInBytes, nVertexStride );
|
|
SetIndexStreamState( nFirstVertexIdx );
|
|
SetTessellationStreamState( nVertexOffsetInBytes, 1 );
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMeshMgr::BindVertexBuffer( int nStreamID, IVertexBuffer *pVertexBuffer, int nOffsetInBytes, int nFirstVertex, int nVertexCount, VertexFormat_t fmt, int nRepetitions )
|
|
{
|
|
// FIXME: Multiple stream support isn't implemented yet
|
|
Assert( nStreamID == 0 );
|
|
|
|
m_pCurrentVertexBuffer = static_cast< CVertexBufferDx8 * >( pVertexBuffer );
|
|
m_CurrentVertexFormat = fmt;
|
|
m_pVertexBufferOffset[nStreamID] = nOffsetInBytes;
|
|
m_pCurrentVertexStride[nStreamID] = m_pCurrentVertexBuffer->VertexSize();
|
|
m_pFirstVertex[nStreamID] = nFirstVertex;
|
|
m_pVertexCount[nStreamID] = nVertexCount,
|
|
m_pVertexIDBuffer = NULL;
|
|
}
|
|
|
|
void CMeshMgr::BindIndexBuffer( IIndexBuffer *pIndexBuffer, int nOffsetInBytes )
|
|
{
|
|
m_pCurrentIndexBuffer = static_cast< CIndexBufferBase * >( pIndexBuffer );
|
|
m_nIndexBufferOffset = nOffsetInBytes;
|
|
}
|
|
|
|
void CMeshMgr::Draw( MaterialPrimitiveType_t primitiveType, int nFirstIndex, int nIndexCount )
|
|
{
|
|
// MESHFIXME : This path is only used for the new index/vertex buffer interfaces.
|
|
// make sure we aren't using a morph stream for this path.
|
|
// Assert( !m_pColorMesh );
|
|
|
|
SetRenderState( m_pVertexBufferOffset[0], /* nFirstVertexIdx */0, m_CurrentVertexFormat, m_pCurrentVertexStride[0] );
|
|
|
|
m_PrimitiveType = MATERIAL_TRIANGLES;
|
|
Assert( primitiveType == MATERIAL_TRIANGLES );
|
|
|
|
m_nFirstIndex = nFirstIndex;
|
|
m_nNumIndices = nIndexCount;
|
|
|
|
ShaderAPI()->DrawWithVertexAndIndexBuffers();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Actually does the dirty deed of rendering
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void CMeshMgr::DrawInstancedPrims( const unsigned char *pInstanceCommandBuffer )
|
|
{
|
|
for ( int i = 0; i < g_nInstanceCount; ++i )
|
|
{
|
|
const MeshInstanceData_t &instance= g_pInstanceData[i];
|
|
int nPrimitiveCount = NumPrimitives( instance.m_nPrimType, instance.m_nIndexCount );
|
|
|
|
CMeshDX8 *pVertexMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pVertexBuffer ) );
|
|
|
|
if ( !pVertexMesh || !pVertexMesh->m_pVertexBuffer )
|
|
{
|
|
Warning( "CMeshMgr::DrawInstancedPrims: Vertex buffer in not setup properly, mesh will not be rendered." );
|
|
continue;
|
|
}
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
pVertexMesh->HandleLateCreation();
|
|
#endif
|
|
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_FLEXDELTA,
|
|
pVertexMesh->m_pVertexBuffer->GetInterface(),
|
|
instance.m_nVertexOffsetInBytes,
|
|
pVertexMesh->m_pVertexBuffer->VertexSize() );
|
|
|
|
D3DSetStreamSource( 0,
|
|
pVertexMesh->m_pVertexBuffer->GetInterface(),
|
|
instance.m_nVertexOffsetInBytes,
|
|
pVertexMesh->m_pVertexBuffer->VertexSize() );
|
|
|
|
g_pLastVertex = pVertexMesh->m_pVertexBuffer;
|
|
g_nLastVertOffsetInBytes = instance.m_nVertexOffsetInBytes;
|
|
|
|
IDirect3DIndexBuffer9 *pD3DIndexBuffer;
|
|
IIndexBuffer *pIndexBuffer = const_cast<IIndexBuffer*>( instance.m_pIndexBuffer );
|
|
if ( pIndexBuffer->GetMesh() )
|
|
{
|
|
CMeshDX8 *pMesh = static_cast<CMeshDX8*>( pIndexBuffer );
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
pMesh->HandleLateCreation();
|
|
#endif
|
|
pD3DIndexBuffer = pMesh->m_pIndexBuffer->GetInterface();
|
|
}
|
|
else
|
|
{
|
|
CIndexBufferDx8 *pCIndexBuffer = static_cast<CIndexBufferDx8*>( pIndexBuffer );
|
|
pD3DIndexBuffer = pCIndexBuffer->m_pIndexBuffer;
|
|
}
|
|
|
|
D3DSetIndices( pD3DIndexBuffer );
|
|
|
|
CMeshDX8 *pColorMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pColorBuffer ) );
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
if (pColorMesh)
|
|
{
|
|
pColorMesh->HandleLateCreation();
|
|
}
|
|
#endif
|
|
CVertexBuffer *pVertexBuffer = pColorMesh ? pColorMesh->GetVertexBuffer() : m_pEmptyColorBuffer;
|
|
int nVertexOffset = pColorMesh ? instance.m_nColorVertexOffsetInBytes : 0;
|
|
|
|
// Set vertex decl
|
|
VertexFormat_t nMeshFormat = pVertexMesh->GetVertexFormat();
|
|
|
|
IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial();
|
|
bool bUseColorMesh = ( pMaterial->GetVertexFormat() & VERTEX_COLOR_STREAM_1 ) != 0;
|
|
ShaderAPI()->SetVertexDecl( nMeshFormat, bUseColorMesh, false, false, false, NULL );
|
|
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SPECULAR1, pVertexBuffer->GetInterface(),
|
|
nVertexOffset, pVertexBuffer->VertexSize() );
|
|
|
|
D3DPRIMITIVETYPE nMode = ComputeMode( instance.m_nPrimType );
|
|
|
|
if ( pInstanceCommandBuffer )
|
|
{
|
|
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, i, true );
|
|
}
|
|
else
|
|
{
|
|
ShaderAPI()->SetSkinningMatrices( instance );
|
|
}
|
|
|
|
{
|
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" );
|
|
VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
|
|
VPROF_INCREMENT_COUNTER( "numPrimitives", nPrimitiveCount );
|
|
|
|
Dx9Device()->DrawIndexedPrimitive(
|
|
nMode, 0, 0,
|
|
pVertexMesh->VertexCount(),
|
|
instance.m_nIndexOffset,
|
|
nPrimitiveCount );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CMeshMgr::RenderPassForInstances( const unsigned char *pInstanceCommandBuffer )
|
|
{
|
|
VPROF( "CMeshMgr::RenderPassForInstances" );
|
|
|
|
IMaterialInternal *pMaterial = ShaderAPI()->GetBoundMaterial();
|
|
bool bUsingVertexID = pMaterial->IsUsingVertexID();
|
|
bool bUseColorMesh = ( pMaterial->GetVertexFormat() & VERTEX_COLOR_STREAM_1 ) != 0;
|
|
|
|
for ( int i = 0; i < g_nInstanceCount; ++i )
|
|
{
|
|
const MeshInstanceData_t &instance = g_pInstanceData[i];
|
|
Assert( ( instance.m_nPrimType != MATERIAL_POINTS ) && ( instance.m_nPrimType != MATERIAL_INSTANCED_QUADS ) );
|
|
int nPrimitiveCount = NumPrimitives( instance.m_nPrimType, instance.m_nIndexCount );
|
|
|
|
// make sure the vertex format is a superset of the current material's
|
|
// vertex format...
|
|
CMeshDX8 *pVertexMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pVertexBuffer ) );
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
pVertexMesh->HandleLateCreation();
|
|
#endif
|
|
|
|
Assert( pVertexMesh );
|
|
VertexFormat_t nMeshFormat = pVertexMesh->GetVertexFormat();
|
|
if ( !IsValidVertexFormat( nMeshFormat, pMaterial ) )
|
|
{
|
|
Warning( "Material %s does not support vertex format used by the mesh\n"
|
|
"(maybe missing fields or mismatched vertex compression?),\n"
|
|
"mesh will not be rendered. Grab a programmer!\n",
|
|
pMaterial->GetName() );
|
|
continue;
|
|
}
|
|
|
|
// FIXME: solve problems when using CVertexBufferDx8 instead of meshes
|
|
pVertexMesh->SetVertexStreamState( instance.m_nVertexOffsetInBytes, true );
|
|
|
|
IIndexBuffer *pIndexBuffer = const_cast<IIndexBuffer*>( instance.m_pIndexBuffer );
|
|
if ( pIndexBuffer->GetMesh() )
|
|
{
|
|
CMeshDX8 *pMesh = static_cast<CMeshDX8*>( pIndexBuffer );
|
|
Assert( pMesh );
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
pMesh->HandleLateCreation();
|
|
#endif
|
|
pMesh->SetIndexStreamState( 0 );
|
|
}
|
|
else
|
|
{
|
|
CIndexBufferDx8 *pCIndexBuffer = static_cast<CIndexBufferDx8*>( pIndexBuffer );
|
|
Assert( pCIndexBuffer );
|
|
pCIndexBuffer->SetIndexStreamState( 0 );
|
|
}
|
|
|
|
CMeshDX8 *pColorMesh = static_cast<CMeshDX8*>( const_cast<IVertexBuffer*>( instance.m_pColorBuffer ) );
|
|
#ifdef DX_TO_GL_ABSTRACTION
|
|
if ( pColorMesh )
|
|
{
|
|
pColorMesh->HandleLateCreation();
|
|
}
|
|
#endif
|
|
CVertexBuffer *pVertexBuffer = pColorMesh ? pColorMesh->GetVertexBuffer() : m_pEmptyColorBuffer;
|
|
int nVertexOffset = pColorMesh ? instance.m_nColorVertexOffsetInBytes : 0;
|
|
D3DSetStreamSource( VertexStreamSpec_t::STREAM_SPECULAR1, pVertexBuffer->GetInterface(), nVertexOffset, pVertexBuffer->VertexSize() );
|
|
pVertexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
g_pLastColorBuffer = pVertexBuffer;
|
|
g_nLastColorMeshVertOffsetInBytes = nVertexOffset;
|
|
|
|
bool bUsingPreTessPatches = ( pVertexMesh->GetTessellationType() > 0 ) && ( ShaderAPI()->GetTessellationMode() == TESSELLATION_MODE_ACC_PATCHES_EXTRA || ShaderAPI()->GetTessellationMode() == TESSELLATION_MODE_ACC_PATCHES_REG );
|
|
ShaderAPI()->SetVertexDecl( nMeshFormat, bUseColorMesh, NULL, bUsingVertexID, bUsingPreTessPatches, NULL );
|
|
|
|
D3DPRIMITIVETYPE nMode = ComputeMode( instance.m_nPrimType );
|
|
|
|
#ifdef CHECK_INDICES
|
|
CheckIndices( nMode, 0, pVertexMesh->VertexCount(), 0, instance.m_nIndexOffset, nPrimitiveCount );
|
|
#endif // CHECK_INDICES
|
|
|
|
ShaderAPI()->ExecuteInstanceCommandBuffer( pInstanceCommandBuffer, i, true );
|
|
|
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" );
|
|
VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
|
|
VPROF_INCREMENT_COUNTER( "numPrimitives", nPrimitiveCount );
|
|
|
|
Dx9Device()->DrawIndexedPrimitive(
|
|
nMode, // Member of the D3DPRIMITIVETYPE enumerated type, describing the type
|
|
// of primitive to render. D3DPT_POINTLIST is not supported with this method.
|
|
|
|
0, // Offset from the start of the vertex buffer to the first vertex index.
|
|
// An index of 0 in the index buffer refers to this location in the vertex buffer.
|
|
|
|
0, // Minimum vertex index for vertices used during this call. This is a zero based
|
|
// index relative to BaseVertexIndex. The first Vertex in the vertexbuffer that
|
|
// we are currently using for the current batch.
|
|
|
|
pVertexMesh->VertexCount(), // Number of vertices used during this call. The first vertex
|
|
// is located at index: BaseVertexIndex + MinIndex.
|
|
|
|
instance.m_nIndexOffset, // Index of the first index to use when accesssing the vertex buffer.
|
|
// Beginning at StartIndex to index vertices from the vertex buffer.
|
|
|
|
nPrimitiveCount ); // Number of primitives to render. The number of vertices used
|
|
// is a function of the primitive count and the primitive type.
|
|
}
|
|
}
|
|
|
|
|
|
void CMeshMgr::RenderPassWithVertexAndIndexBuffers( const unsigned char *pInstanceCommandBuffer )
|
|
{
|
|
// LOCK_SHADERAPI(); MESHFIXME
|
|
VPROF( "CShaderAPIDX8::RenderPassWithVertexAndIndexBuffers" );
|
|
|
|
if ( g_nInstanceCount )
|
|
{
|
|
RenderPassForInstances( pInstanceCommandBuffer );
|
|
return;
|
|
}
|
|
|
|
Assert( m_PrimitiveType != MATERIAL_HETEROGENOUS );
|
|
|
|
// for ( int iPrim=0; iPrim < s_nPrims; iPrim++ )
|
|
{
|
|
// CPrimList *pPrim = &s_pPrims[iPrim];
|
|
|
|
// if ( pPrim->m_NumIndices == 0 )
|
|
// continue;
|
|
|
|
if ( m_PrimitiveType == MATERIAL_POINTS )
|
|
{
|
|
// (For point lists, we don't actually fill in indices, but we treat it as
|
|
// though there are indices for the list up until here).
|
|
Assert( 0 );
|
|
// Dx9Device()->DrawPrimitive( ComputeMode( m_PrimitiveType ), s_FirstVertex, pPrim->m_NumIndices );
|
|
}
|
|
else
|
|
{
|
|
// int numPrimitives = NumPrimitives( s_NumVertices, pPrim->m_NumIndices );
|
|
|
|
// Warning( "CMeshMgr::RenderPassWithVertexAndIndexBuffers: DrawIndexedPrimitive: m_nFirstIndex = %d numPrimitives = %d\n", ( int )( ( CDynamiCIndexBufferDx8 * )m_pCurrentIndexBuffer )->m_FirstIndex, ( int )( m_nNumIndices / 3 ) );
|
|
{
|
|
VPROF( "Dx9Device()->DrawIndexedPrimitive" );
|
|
// VPROF_INCREMENT_COUNTER( "DrawIndexedPrimitive", 1 );
|
|
// VPROF_INCREMENT_COUNTER( "numPrimitives", numPrimitives );
|
|
|
|
// Dx9Device()->DrawIndexedPrimitive(
|
|
// m_Mode,
|
|
// m_FirstIndex,
|
|
// s_FirstVertex,
|
|
// s_NumVertices,
|
|
// pPrim->m_FirstIndex,
|
|
// numPrimitives );
|
|
|
|
Assert( m_nFirstIndex >= 0 );
|
|
|
|
#ifdef CHECK_INDICES
|
|
// g_pLastVertex - this is the current vertex buffer
|
|
// g_pLastColorBuffer - this is the curent color mesh, if there is one.
|
|
// g_pLastIndex - this is the current index buffer.
|
|
// vertoffset : m_FirstIndex
|
|
CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8 * >( m_pCurrentIndexBuffer );
|
|
if( m_PrimitiveType == MATERIAL_TRIANGLES || m_PrimitiveType == MATERIAL_TRIANGLE_STRIP )
|
|
{
|
|
// FIXME: need to be able to deal with multiple stream here, but don't bother for now.
|
|
int j;
|
|
int numVerts = m_pVertexCount[0];
|
|
for( j = 0; j < m_nNumIndices; j++ )
|
|
{
|
|
int index = pIndexBuffer->GetShadowIndex( j + m_nFirstIndex );
|
|
Assert( index >= m_pFirstVertex[0] );
|
|
Assert( index < m_pFirstVertex[0] + numVerts );
|
|
}
|
|
}
|
|
#endif // CHECK_INDICES
|
|
Dx9Device()->DrawIndexedPrimitive(
|
|
ComputeMode( m_PrimitiveType ), // Member of the D3DPRIMITIVETYPE enumerated type, describing the type of primitive to render. D3DPT_POINTLIST is not supported with this method.
|
|
|
|
/*m_FirstIndex*/ 0, // Offset from the start of the vertex buffer to the first vertex index. An index of 0 in the index buffer refers to this location in the vertex buffer.
|
|
|
|
/*s_FirstVertex*/ m_pFirstVertex[0],// Minimum vertex index for vertices used during this call. This is a zero based index relative to BaseVertexIndex.
|
|
// This is zero for now since we don't do more than one batch yet with the new mesh interface.
|
|
|
|
/*s_NumVertices*/ m_pVertexCount[0],
|
|
// Number of vertices used during this call. The first vertex is located at index: BaseVertexIndex + MinIndex.
|
|
// This is simple the number of verts in the current vertex buffer for now since we don't do more than one batch with the new mesh interface.
|
|
|
|
m_nFirstIndex /*pPrim->m_FirstIndex*/, // Index of the first index to use when accesssing the vertex buffer. Beginning at StartIndex to index vertices from the vertex buffer.
|
|
|
|
m_nNumIndices / 3/*numPrimitives*/ // Number of primitives to render. The number of vertices used is a function of the primitive count and the primitive type.
|
|
);
|
|
|
|
Assert( CMeshDX8::s_FirstVertex == 0 );
|
|
Assert( CMeshDX8::s_NumVertices == 0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void CMeshMgr::SetIndexStreamState( int firstVertexIdx )
|
|
{
|
|
CIndexBufferDx8 *pIndexBuffer = assert_cast< CIndexBufferDx8* >( m_pCurrentIndexBuffer );
|
|
IDirect3DIndexBuffer9 *pDx9Buffer = pIndexBuffer ? pIndexBuffer->GetDx9Buffer() : NULL;
|
|
if ( g_pLastIndex || g_pLastIndexBuffer != pDx9Buffer )
|
|
{
|
|
D3DSetIndices( pDx9Buffer );
|
|
pIndexBuffer->HandlePerFrameTextureStats( ShaderAPI()->GetCurrentFrameCounter() );
|
|
|
|
g_pLastIndex = NULL;
|
|
g_LastVertexIdx = -1;
|
|
}
|
|
}
|
|
|
|
|