//---------------------------------------------------------------------------- // File: SmartVB.h // // Desc: see main.cpp // // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved. //----------------------------------------------------------------------------- #ifndef _SMARTVB_H #define _SMARTVB_H #include "DXUtil.h" //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- // Smart vertex buffer template class. This performs the usual NOOVERWRITE/DISCARDCONTENTS // filling loop. Simply call Begin(), then multiple instances of MakeRoom() to be given // pointers to where to store vertex and index data, then End() when done. The class // automatically flushes rendering as the buffer fills. // // By default rendering is performed by calling DrawIndexedPrimitive with a TRIANGELIST // consisting of the submitted data, but the client can supply a callback if more complex // rendering is required (perhaps for multipass?). typedef void (__stdcall *SmartVBRenderCallback)( LPDIRECT3DVERTEXBUFFER8 pVB, DWORD dwStartVertex, DWORD dwNumVertices, LPDIRECT3DINDEXBUFFER8 pIB, DWORD dwStartIndex, DWORD dwIndexCount, void* pParam ); //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- template< class VertType, DWORD VertFVF, DWORD NumIndex > class SmartVB { public: //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- SmartVB() : m_pVB(NULL), m_pd3dDevice(NULL), m_pIB(NULL) { SetRenderCallback(); }; //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- ~SmartVB() { Uninit(); }; //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- HRESULT Init( IDirect3D8* pD3D, IDirect3DDevice8* pDevice, DWORD dwNumVerts ) { HRESULT hr; Uninit(); m_dwNumVerts = dwNumVerts; m_pd3dDevice = pDevice; if( m_pd3dDevice ) m_pd3dDevice->AddRef(); else return E_INVALIDARG; m_pNextIndex = NULL; m_dwNextIndex = 0; if( FAILED( hr = m_pd3dDevice->CreateIndexBuffer( NumIndex * sizeof(WORD), D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pIB ) ) ) return hr; m_pNextVert = NULL; m_dwNextVert = 0; DWORD dwFVFSize = D3DXGetFVFVertexSize( VertFVF ); if( FAILED( hr = m_pd3dDevice->CreateVertexBuffer( dwFVFSize * dwNumVerts, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, VertFVF, D3DPOOL_DEFAULT, &m_pVB ) ) ) return hr; return S_OK; }; //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- void Uninit() { SAFE_RELEASE( m_pVB ); SAFE_RELEASE( m_pIB ); SAFE_RELEASE( m_pd3dDevice ); }; //----------------------------------------------------------------------------- // Name: // Desc: Set rendering callback. Passing NULL for the callback gets you a default // call to DrawIndexedPrimitive. pParam is passed through to the callback. //----------------------------------------------------------------------------- void SetRenderCallback( SmartVBRenderCallback pCallback = NULL, void* pParam = NULL ) { if ( pCallback != NULL ) { m_pCallback = pCallback; m_pCallbackParam = pParam; } else { m_pCallback = DefaultRenderCallback; m_pCallbackParam = this; } }; //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- HRESULT Begin() { // Check we haven't already begun if ( m_pNextVert != NULL && m_pNextIndex != NULL ) return E_FAIL; // Lock buffer, use D3DLOCK_DISCARD flag m_pVB->Lock( 0, 0, (BYTE**)&m_pNextVert, D3DLOCK_DISCARD ); m_pNextVert += m_dwNextVert; m_dwFirstVert = m_dwNextVert; m_dwVertexCount = 0; m_pIB->Lock( 0, 0, (BYTE**)&m_pNextIndex, D3DLOCK_DISCARD ); m_pNextIndex += m_dwNextIndex; m_dwFirstIndex = m_dwNextIndex; m_dwIndexCount = 0; return S_OK; }; //----------------------------------------------------------------------------- // Name: // Desc: Request space to submit data, may cause a 'flush' for rendering //----------------------------------------------------------------------------- HRESULT MakeRoom( DWORD dwNumVert, DWORD dwNumIndex, VertType** pVertPtr, WORD** pIndexPtr, WORD* wIndexOffset ) { Flush(); // Have we room left in the buffer? if ( (dwNumVert + m_dwNextVert >= m_dwNumVerts) || (dwNumIndex + m_dwNextIndex >= NumIndex) ) { // Nope, so flush current batch Flush(); // Check we could fit this in at all if ( (dwNumVert > m_dwNumVerts) || (dwNumIndex > NumIndex) ) return E_INVALIDARG; } // Got room, so just return position in buffer *pVertPtr = m_pNextVert; *pIndexPtr = m_pNextIndex; *wIndexOffset = WORD(m_dwVertexCount); // Update position m_pNextVert += dwNumVert; m_dwNextVert += dwNumVert; m_dwVertexCount += dwNumVert; m_pNextIndex += dwNumIndex; m_dwNextIndex += dwNumIndex; m_dwIndexCount += dwNumIndex; // Done return S_OK; }; //----------------------------------------------------------------------------- // Name: // Desc: End data filling, and submit for rendering via callback //----------------------------------------------------------------------------- void End() { // Unlock VB m_pVB->Unlock(); m_pIB->Unlock(); // Submit for rendering if ( m_dwVertexCount > 0 && m_dwIndexCount > 0 ) m_pCallback( m_pVB, m_dwFirstVert, m_dwVertexCount, m_pIB, m_dwFirstIndex, m_dwIndexCount, m_pCallbackParam ); m_pNextVert = NULL; m_pNextIndex = NULL; }; //----------------------------------------------------------------------------- // Name: // Desc: Flush data if we overflowed //----------------------------------------------------------------------------- void Flush() { // Unlock VB m_pVB->Unlock(); m_pIB->Unlock(); // Submit for rendering if ( m_dwVertexCount > 0 && m_dwIndexCount > 0 ) m_pCallback( m_pVB, m_dwFirstVert, m_dwVertexCount, m_pIB, m_dwFirstIndex, m_dwIndexCount, m_pCallbackParam ); // Lock VB again m_pVB->Lock( 0, 0, (BYTE**)&m_pNextVert, D3DLOCK_DISCARD ); m_dwFirstVert = 0; m_dwVertexCount = 0; m_dwNextVert = 0; m_pIB->Lock( 0, 0, (BYTE**)&m_pNextIndex, D3DLOCK_DISCARD ); m_dwFirstIndex = 0; m_dwIndexCount = 0; m_dwNextIndex = 0; }; protected: IDirect3DDevice8* m_pd3dDevice; DWORD m_dwNumVerts; SmartVBRenderCallback m_pCallback; void* m_pCallbackParam; // Current position of 'write cursor' in buffer LPDIRECT3DINDEXBUFFER8 m_pIB; WORD* m_pNextIndex; DWORD m_dwNextIndex; DWORD m_dwFirstIndex; LPDIRECT3DVERTEXBUFFER8 m_pVB; VertType* m_pNextVert; DWORD m_dwNextVert; DWORD m_dwFirstVert; // Counts of vertices and indicies in the current batch DWORD m_dwVertexCount; DWORD m_dwIndexCount; static void __stdcall DefaultRenderCallback( LPDIRECT3DVERTEXBUFFER8 pVB, DWORD dwStartVertex, DWORD dwNumVertices, LPDIRECT3DINDEXBUFFER8 pIB, DWORD dwStartIndex, DWORD dwIndexCount, void* pParam ) { ((SmartVB*)pParam)->m_pd3dDevice->SetVertexShader( VertFVF ); ((SmartVB*)pParam)->m_pd3dDevice->SetStreamSource( 0, pVB, sizeof(VertType) ); ((SmartVB*)pParam)->m_pd3dDevice->SetIndices( pIB, 0 ); ((SmartVB*)pParam)->m_pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, dwStartVertex, dwNumVertices, dwStartIndex, dwIndexCount/3 ); }; private: SmartVB( const SmartVB& ); void operator=( const SmartVB& ); }; #endif