//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //===========================================================================// #ifndef IMESH_H #define IMESH_H #ifdef _WIN32 #pragma once #endif #include "tier1/interface.h" #include "materialsystem/imaterial.h" #include #include #include "tier0/dbg.h" #include "tier2/meshutils.h" #include "mathlib/mathlib.h" #if defined( DX_TO_GL_ABSTRACTION ) // Swap these so that we do color swapping on 10.6.2, which doesn't have EXT_vertex_array_bgra #define OPENGL_SWAP_COLORS #endif //----------------------------------------------------------------------------- // forward declarations //----------------------------------------------------------------------------- class IMaterial; class CMeshBuilder; class IMaterialVar; typedef uint64 VertexFormat_t; //----------------------------------------------------------------------------- // Define this to find write-combine problems //----------------------------------------------------------------------------- #ifdef _DEBUG //#ifndef DEBUG_WRITE_COMBINE //#define DEBUG_WRITE_COMBINE 1 //#endif #endif //----------------------------------------------------------------------------- // The Vertex Buffer interface //----------------------------------------------------------------------------- enum { VERTEX_MAX_TEXTURE_COORDINATES = 8, BONE_MATRIX_INDEX_INVALID = 255 }; // Internal maximums for sizes. Don't use directly, use IMaterialSystem::GetMaxToRender() enum { INDEX_BUFFER_SIZE = 32768, DYNAMIC_VERTEX_BUFFER_MEMORY = ( 1024 + 512 ) * 1024, DYNAMIC_VERTEX_BUFFER_MEMORY_SMALL = 384 * 1024, // Only allocate this much during map transitions }; // Vertex fields must be written in well-defined order to achieve write combining, // which is a perf booster enum WriteCombineOrdering_t { MB_FIELD_NONE = -1, MB_FIELD_POSITION = 0, MB_FIELD_BONE_WEIGHTS, MB_FIELD_BONE_INDEX, MB_FIELD_NORMAL, MB_FIELD_COLOR, MB_FIELD_SPECULAR, MB_FIELD_TEXCOORD_FIRST, MB_FIELD_TEXCOORD_LAST = MB_FIELD_TEXCOORD_FIRST + VERTEX_MAX_TEXTURE_COORDINATES - 1, MB_FIELD_TANGENT_S, MB_FIELD_TANGENT_T, MB_FIELD_USERDATA, }; #define MB_FIELD_TEXCOORD( nStage ) ( MB_FIELD_TEXCOORD_FIRST + ( nStage ) ) struct VertexDesc_t { // These can be set to zero if there are pointers to dummy buffers, when the // actual buffer format doesn't contain the data but it needs to be safe to // use all the CMeshBuilder functions. int m_VertexSize_Position; int m_VertexSize_BoneWeight; int m_VertexSize_BoneMatrixIndex; int m_VertexSize_Normal; int m_VertexSize_Color; int m_VertexSize_Specular; int m_VertexSize_TexCoord[VERTEX_MAX_TEXTURE_COORDINATES]; int m_VertexSize_TangentS; int m_VertexSize_TangentT; int m_VertexSize_Wrinkle; int m_VertexSize_UserData; int m_ActualVertexSize; // Size of the vertices.. Some of the m_VertexSize_ elements above // are set to this value and some are set to zero depending on which // fields exist in a buffer's vertex format. // The type of compression applied to this vertex data VertexCompressionType_t m_CompressionType; // Number of bone weights per vertex... int m_NumBoneWeights; // Pointers to our current vertex data float *m_pPosition; float *m_pBoneWeight; #ifndef NEW_SKINNING unsigned char *m_pBoneMatrixIndex; #else float *m_pBoneMatrixIndex; #endif float *m_pNormal; unsigned char *m_pColor; unsigned char *m_pSpecular; float *m_pTexCoord[VERTEX_MAX_TEXTURE_COORDINATES]; // Tangent space *associated with one particular set of texcoords* float *m_pTangentS; float *m_pTangentT; float *m_pWrinkle; // user data float *m_pUserData; // The first vertex index (used for buffered vertex buffers, or cards that don't support stream offset) int m_nFirstVertex; // The offset in bytes of the memory we're writing into // from the start of the D3D buffer (will be 0 for static meshes) unsigned int m_nOffset; #ifdef DEBUG_WRITE_COMBINE int m_nLastWrittenField; unsigned char* m_pLastWrittenAddress; #endif }; struct IndexDesc_t { // Pointers to the index data unsigned short *m_pIndices; // The offset in bytes of the memory we're writing into // from the start of the D3D buffer (will be 0 for static meshes) unsigned int m_nOffset; // The first index (used for buffered index buffers, or cards that don't support stream offset) unsigned int m_nFirstIndex; // 1 if the device is active, 0 if the device isn't active. // Faster than doing if checks for null m_pIndices if someone is // trying to write the m_pIndices while the device is inactive. unsigned char m_nIndexSize; }; //----------------------------------------------------------------------------- // The Mesh memory descriptor //----------------------------------------------------------------------------- struct MeshDesc_t : public VertexDesc_t, public IndexDesc_t { }; //----------------------------------------------------------------------------- // Standard vertex formats for models //----------------------------------------------------------------------------- struct ModelVertexDX7_t { Vector m_vecPosition; Vector2D m_flBoneWeights; unsigned int m_nBoneIndices; Vector m_vecNormal; unsigned int m_nColor; // ARGB Vector2D m_vecTexCoord; }; struct ModelVertexDX8_t : public ModelVertexDX7_t { Vector4D m_vecUserData; }; //----------------------------------------------------------------------------- // Utility methods for buffer builders //----------------------------------------------------------------------------- inline float *OffsetFloatPointer( float *pBufferPointer, int nVertexCount, int vertexSize ) { return reinterpret_cast( reinterpret_cast(pBufferPointer) + nVertexCount * vertexSize); } inline const float *OffsetFloatPointer( const float *pBufferPointer, int nVertexCount, int vertexSize ) { return reinterpret_cast( reinterpret_cast(pBufferPointer) + nVertexCount * vertexSize); } inline void IncrementFloatPointer( float* &pBufferPointer, int vertexSize ) { pBufferPointer = reinterpret_cast( reinterpret_cast( pBufferPointer ) + vertexSize ); } //----------------------------------------------------------------------------- // Used in lists of indexed primitives. //----------------------------------------------------------------------------- class CPrimList { public: CPrimList(); CPrimList( int nFirstIndex, int nIndexCount ); int m_FirstIndex; int m_NumIndices; }; inline CPrimList::CPrimList() { } inline CPrimList::CPrimList( int nFirstIndex, int nIndexCount ) { m_FirstIndex = nFirstIndex; m_NumIndices = nIndexCount; } abstract_class IVertexBuffer { public: // Add a virtual destructor to silence the clang warning. // This is harmless but not important since the only derived class // doesn't have a destructor. virtual ~IVertexBuffer() {} // NOTE: The following two methods are only valid for static vertex buffers // Returns the number of vertices and the format of the vertex buffer virtual int VertexCount() const = 0; virtual VertexFormat_t GetVertexFormat() const = 0; // Is this vertex buffer dynamic? virtual bool IsDynamic() const = 0; // NOTE: For dynamic vertex buffers only! // Casts the memory of the dynamic vertex buffer to the appropriate type virtual void BeginCastBuffer( VertexFormat_t format ) = 0; virtual void EndCastBuffer() = 0; // Returns the number of vertices that can still be written into the buffer virtual int GetRoomRemaining() const = 0; virtual bool Lock( int nVertexCount, bool bAppend, VertexDesc_t &desc ) = 0; virtual void Unlock( int nVertexCount, VertexDesc_t &desc ) = 0; // Spews the mesh data virtual void Spew( int nVertexCount, const VertexDesc_t &desc ) = 0; // Call this in debug mode to make sure our data is good. virtual void ValidateData( int nVertexCount, const VertexDesc_t & desc ) = 0; }; abstract_class IIndexBuffer { public: // Add a virtual destructor to silence the clang warning. // This is harmless but not important since the only derived class // doesn't have a destructor. virtual ~IIndexBuffer() {} // NOTE: The following two methods are only valid for static index buffers // Returns the number of indices and the format of the index buffer virtual int IndexCount() const = 0; virtual MaterialIndexFormat_t IndexFormat() const = 0; // Is this index buffer dynamic? virtual bool IsDynamic() const = 0; // NOTE: For dynamic index buffers only! // Casts the memory of the dynamic index buffer to the appropriate type virtual void BeginCastBuffer( MaterialIndexFormat_t format ) = 0; virtual void EndCastBuffer() = 0; // Returns the number of indices that can still be written into the buffer virtual int GetRoomRemaining() const = 0; // Locks, unlocks the index buffer virtual bool Lock( int nMaxIndexCount, bool bAppend, IndexDesc_t &desc ) = 0; virtual void Unlock( int nWrittenIndexCount, IndexDesc_t &desc ) = 0; // FIXME: Remove this!! // Locks, unlocks the index buffer for modify virtual void ModifyBegin( bool bReadOnly, int nFirstIndex, int nIndexCount, IndexDesc_t& desc ) = 0; virtual void ModifyEnd( IndexDesc_t& desc ) = 0; // Spews the mesh data virtual void Spew( int nIndexCount, const IndexDesc_t &desc ) = 0; // Ensures the data in the index buffer is valid virtual void ValidateData( int nIndexCount, const IndexDesc_t &desc ) = 0; }; //----------------------------------------------------------------------------- // Interface to the mesh - needs to contain an IVertexBuffer and an IIndexBuffer to emulate old mesh behavior //----------------------------------------------------------------------------- abstract_class IMesh : public IVertexBuffer, public IIndexBuffer { public: // ----------------------------------- // Sets/gets the primitive type virtual void SetPrimitiveType( MaterialPrimitiveType_t type ) = 0; // Draws the mesh virtual void Draw( int nFirstIndex = -1, int nIndexCount = 0 ) = 0; virtual void SetColorMesh( IMesh *pColorMesh, int nVertexOffset ) = 0; // Draw a list of (lists of) primitives. Batching your lists together that use // the same lightmap, material, vertex and index buffers with multipass shaders // can drastically reduce state-switching overhead. // NOTE: this only works with STATIC meshes. virtual void Draw( CPrimList *pLists, int nLists ) = 0; // 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 ) = 0; // Spews the mesh data virtual void Spew( int nVertexCount, int nIndexCount, const MeshDesc_t &desc ) = 0; // Call this in debug mode to make sure our data is good. virtual void ValidateData( int nVertexCount, int nIndexCount, const MeshDesc_t &desc ) = 0; // New version // Locks/unlocks the mesh, providing space for nVertexCount and nIndexCount. // nIndexCount of -1 means don't lock the index buffer... virtual void LockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ) = 0; virtual void ModifyBegin( int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t& desc ) = 0; virtual void ModifyEnd( MeshDesc_t& desc ) = 0; virtual void UnlockMesh( int nVertexCount, int nIndexCount, MeshDesc_t &desc ) = 0; virtual void ModifyBeginEx( bool bReadOnly, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount, MeshDesc_t &desc ) = 0; virtual void SetFlexMesh( IMesh *pMesh, int nVertexOffset ) = 0; virtual void DisableFlexMesh() = 0; virtual void MarkAsDrawn() = 0; virtual unsigned ComputeMemoryUsed() = 0; }; #include "meshreader.h" #define INVALID_BUFFER_OFFSET 0xFFFFFFFFUL // flags for advancevertex optimization #define VTX_HAVEPOS 1 #define VTX_HAVENORMAL 2 #define VTX_HAVECOLOR 4 #define VTX_HAVEALL ( VTX_HAVEPOS | VTX_HAVENORMAL | VTX_HAVECOLOR ) //----------------------------------------------------------------------------- // // Helper class used to define vertex buffers // //----------------------------------------------------------------------------- class CVertexBuilder : private VertexDesc_t { public: CVertexBuilder(); CVertexBuilder( IVertexBuffer *pVertexBuffer, VertexFormat_t fmt = 0 ); ~CVertexBuilder(); // Begins, ends modification of the index buffer (returns true if the lock succeeded) // A lock may not succeed if append is set to true and there isn't enough room // NOTE: Append is only used with dynamic index buffers; it's ignored for static buffers bool Lock( int nMaxIndexCount, bool bAppend = false ); void Unlock(); // Spews the current data // NOTE: Can only be called during a lock/unlock block void SpewData(); // Returns the number of indices we can fit into the buffer without needing to discard int GetRoomRemaining() const; // Binds this vertex buffer void Bind( IMatRenderContext *pContext, int nStreamID, VertexFormat_t usage = 0 ); // Returns the byte offset int Offset() const; // This must be called before Begin, if a vertex buffer with a compressed format is to be used void SetCompressionType( VertexCompressionType_t compressionType ); void ValidateCompressionType(); void Begin( IVertexBuffer *pVertexBuffer, int nVertexCount, int *nFirstVertex ); void Begin( IVertexBuffer *pVertexBuffer, int nVertexCount ); // Use this when you're done writing // Set bDraw to true to call m_pMesh->Draw automatically. void End( bool bSpewData = false ); // Locks the vertex buffer to modify existing data // Passing nVertexCount == -1 says to lock all the vertices for modification. void BeginModify( IVertexBuffer *pVertexBuffer, int nFirstVertex = 0, int nVertexCount = -1 ); void EndModify( bool bSpewData = false ); // returns the number of vertices int VertexCount() const; // Returns the total number of vertices across all Locks() int TotalVertexCount() const; // Resets the mesh builder so it points to the start of everything again void Reset(); // Returns the size of the vertex int VertexSize() { return m_ActualVertexSize; } // returns the data size of a given texture coordinate int TextureCoordinateSize( int nTexCoordNumber ) { return m_VertexSize_TexCoord[ nTexCoordNumber ]; } // Returns the base vertex memory pointer void* BaseVertexData(); // Selects the nth Vertex and Index void SelectVertex( int idx ); // Advances the current vertex and index by one void AdvanceVertex( void ); template void AdvanceVertexF( void ); void AdvanceVertices( int nVerts ); int GetCurrentVertex() const; int GetFirstVertex() const; // Data retrieval... const float *Position() const; const float *Normal() const; unsigned int Color() const; unsigned char *Specular() const; const float *TexCoord( int stage ) const; const float *TangentS() const; const float *TangentT() const; const float *BoneWeight() const; float Wrinkle() const; int NumBoneWeights() const; #ifndef NEW_SKINNING unsigned char *BoneMatrix() const; #else float *BoneMatrix() const; #endif // position setting void Position3f( float x, float y, float z ); void Position3fv( const float *v ); // normal setting void Normal3f( float nx, float ny, float nz ); void Normal3fv( const float *n ); void NormalDelta3fv( const float *n ); void NormalDelta3f( float nx, float ny, float nz ); // normal setting (templatized for code which needs to support compressed vertices) template void CompressedNormal3f( float nx, float ny, float nz ); template void CompressedNormal3fv( const float *n ); // color setting void Color3f( float r, float g, float b ); void Color3fv( const float *rgb ); void Color4f( float r, float g, float b, float a ); void Color4fv( const float *rgba ); // Faster versions of color void Color3ub( unsigned char r, unsigned char g, unsigned char b ); void Color3ubv( unsigned char const* rgb ); void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); void Color4ubv( unsigned char const* rgba ); // specular color setting void Specular3f( float r, float g, float b ); void Specular3fv( const float *rgb ); void Specular4f( float r, float g, float b, float a ); void Specular4fv( const float *rgba ); // Faster version of specular void Specular3ub( unsigned char r, unsigned char g, unsigned char b ); void Specular3ubv( unsigned char const *c ); void Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); void Specular4ubv( unsigned char const *c ); // texture coordinate setting void TexCoord1f( int stage, float s ); void TexCoord2f( int stage, float s, float t ); void TexCoord2fv( int stage, const float *st ); void TexCoord3f( int stage, float s, float t, float u ); void TexCoord3fv( int stage, const float *stu ); void TexCoord4f( int stage, float s, float t, float u, float w ); void TexCoord4fv( int stage, const float *stuv ); void TexCoordSubRect2f( int stage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ); void TexCoordSubRect2fv( int stage, const float *st, const float *offset, const float *scale ); // tangent space void TangentS3f( float sx, float sy, float sz ); void TangentS3fv( const float* s ); void TangentT3f( float tx, float ty, float tz ); void TangentT3fv( const float* t ); // Wrinkle void Wrinkle1f( float flWrinkle ); // bone weights void BoneWeight( int idx, float weight ); // bone weights (templatized for code which needs to support compressed vertices) template void CompressedBoneWeight3fv( const float * pWeights ); // bone matrix index void BoneMatrix( int idx, int matrixIndex ); // Generic per-vertex data void UserData( const float* pData ); // Generic per-vertex data (templatized for code which needs to support compressed vertices) template void CompressedUserData( const float* pData ); // Fast Vertex! No need to call advance vertex, and no random access allowed. // WARNING - these are low level functions that are intended only for use // in the software vertex skinner. void FastVertex( const ModelVertexDX7_t &vertex ); void FastVertexSSE( const ModelVertexDX7_t &vertex ); // store 4 dx7 vertices fast. for special sse dx7 pipeline void Fast4VerticesSSE( ModelVertexDX7_t const *vtx_a, ModelVertexDX7_t const *vtx_b, ModelVertexDX7_t const *vtx_c, ModelVertexDX7_t const *vtx_d); void FastVertex( const ModelVertexDX8_t &vertex ); void FastVertexSSE( const ModelVertexDX8_t &vertex ); // Add number of verts and current vert since FastVertex routines do not update. void FastAdvanceNVertices( int n ); #if defined( _X360 ) void VertexDX8ToX360( const ModelVertexDX8_t &vertex ); #endif // FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. void AttachBegin( IMesh* pMesh, int nMaxVertexCount, const MeshDesc_t &desc ); void AttachEnd(); void AttachBeginModify( IMesh* pMesh, int nFirstVertex, int nVertexCount, const MeshDesc_t &desc ); void AttachEndModify(); private: // The vertex buffer we're modifying IVertexBuffer *m_pVertexBuffer; // Used to make sure Begin/End calls and BeginModify/EndModify calls match. bool m_bModify; // Max number of indices and vertices int m_nMaxVertexCount; // Number of indices and vertices int m_nVertexCount; // The current vertex and index mutable int m_nCurrentVertex; // Optimization: Pointer to the current pos, norm, texcoord, and color mutable float *m_pCurrPosition; mutable float *m_pCurrNormal; mutable float *m_pCurrTexCoord[VERTEX_MAX_TEXTURE_COORDINATES]; mutable unsigned char *m_pCurrColor; // Total number of vertices appended int m_nTotalVertexCount; // First vertex buffer offset + index unsigned int m_nBufferOffset; unsigned int m_nBufferFirstVertex; #if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) // Debug checks to make sure we write userdata4/tangents AFTER normals bool m_bWrittenNormal : 1; bool m_bWrittenUserData : 1; #endif friend class CMeshBuilder; }; //----------------------------------------------------------------------------- // // Inline methods of CVertexBuilder // //----------------------------------------------------------------------------- inline CVertexBuilder::CVertexBuilder() { m_pVertexBuffer = NULL; m_nBufferOffset = INVALID_BUFFER_OFFSET; m_nBufferFirstVertex = 0; m_nVertexCount = 0; m_nCurrentVertex = 0; m_nMaxVertexCount = 0; m_nTotalVertexCount = 0; m_CompressionType = VERTEX_COMPRESSION_INVALID; #ifdef _DEBUG m_pCurrPosition = NULL; m_pCurrNormal = NULL; m_pCurrColor = NULL; memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); m_bModify = false; #endif } inline CVertexBuilder::CVertexBuilder( IVertexBuffer *pVertexBuffer, VertexFormat_t fmt ) { m_pVertexBuffer = pVertexBuffer; m_nBufferOffset = INVALID_BUFFER_OFFSET; m_nBufferFirstVertex = 0; m_nVertexCount = 0; m_nCurrentVertex = 0; m_nMaxVertexCount = 0; m_nTotalVertexCount = 0; m_CompressionType = VERTEX_COMPRESSION_INVALID; if ( m_pVertexBuffer->IsDynamic() ) { m_pVertexBuffer->BeginCastBuffer( fmt ); } else { Assert( m_pVertexBuffer->GetVertexFormat() == fmt ); } #ifdef _DEBUG m_pCurrPosition = NULL; m_pCurrNormal = NULL; m_pCurrColor = NULL; memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); m_bModify = false; #endif } inline CVertexBuilder::~CVertexBuilder() { if ( m_pVertexBuffer && m_pVertexBuffer->IsDynamic() ) { m_pVertexBuffer->EndCastBuffer(); } } //----------------------------------------------------------------------------- // Begins, ends modification of the index buffer //----------------------------------------------------------------------------- inline bool CVertexBuilder::Lock( int nMaxVertexCount, bool bAppend ) { Assert( m_pVertexBuffer ); m_bModify = false; m_nMaxVertexCount = nMaxVertexCount; bool bFirstLock = ( m_nBufferOffset == INVALID_BUFFER_OFFSET ); if ( bFirstLock ) { bAppend = false; } if ( !bAppend ) { m_nTotalVertexCount = 0; } // Lock the vertex buffer if ( !m_pVertexBuffer->Lock( m_nMaxVertexCount, bAppend, *this ) ) { m_nMaxVertexCount = 0; return false; } Reset(); if ( bFirstLock ) { m_nBufferOffset = m_nOffset; m_nBufferFirstVertex = m_nFirstVertex; } return true; } inline void CVertexBuilder::Unlock() { Assert( !m_bModify && m_pVertexBuffer ); #ifdef _DEBUG m_pVertexBuffer->ValidateData( m_nVertexCount, *this ); #endif m_pVertexBuffer->Unlock( m_nVertexCount, *this ); m_nTotalVertexCount += m_nVertexCount; m_nMaxVertexCount = 0; #ifdef _DEBUG // Null out our data... m_pCurrPosition = NULL; m_pCurrNormal = NULL; m_pCurrColor = NULL; memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); memset( static_cast( this ), 0, sizeof(VertexDesc_t) ); #endif } inline void CVertexBuilder::SpewData() { m_pVertexBuffer->Spew( m_nVertexCount, *this ); } //----------------------------------------------------------------------------- // Binds this vertex buffer //----------------------------------------------------------------------------- inline void CVertexBuilder::Bind( IMatRenderContext *pContext, int nStreamID, VertexFormat_t usage ) { if ( m_pVertexBuffer && ( m_nBufferOffset != INVALID_BUFFER_OFFSET ) ) { pContext->BindVertexBuffer( nStreamID, m_pVertexBuffer, m_nBufferOffset, m_nFirstVertex, m_nTotalVertexCount, usage ? usage : m_pVertexBuffer->GetVertexFormat() ); } else { pContext->BindVertexBuffer( nStreamID, NULL, 0, 0, 0, 0 ); } } //----------------------------------------------------------------------------- // Returns the byte offset //----------------------------------------------------------------------------- inline int CVertexBuilder::Offset() const { return m_nBufferOffset; } inline int CVertexBuilder::GetFirstVertex() const { return m_nBufferFirstVertex; } //----------------------------------------------------------------------------- // Specify the type of vertex compression that this CMeshBuilder will perform //----------------------------------------------------------------------------- inline void CVertexBuilder::SetCompressionType( VertexCompressionType_t compressionType ) { // The real purpose of this method is to allow us to emit a Warning in Begin() m_CompressionType = compressionType; } inline void CVertexBuilder::ValidateCompressionType() { #ifdef _DEBUG VertexCompressionType_t vbCompressionType = CompressionType( m_pVertexBuffer->GetVertexFormat() ); if ( vbCompressionType != VERTEX_COMPRESSION_NONE ) { Assert( m_CompressionType == vbCompressionType ); if ( m_CompressionType != vbCompressionType ) { Warning( "ERROR: CVertexBuilder::SetCompressionType() must be called to specify the same vertex compression type (%s) as the vertex buffer being modified." "Junk vertices will be rendered, or there will be a crash in CVertexBuilder!\n", vbCompressionType == VERTEX_COMPRESSION_ON ? "VERTEX_COMPRESSION_ON" : "VERTEX_COMPRESSION_NONE" ); } // Never use vertex compression for dynamic VBs (the conversions can really hurt perf) Assert( !m_pVertexBuffer->IsDynamic() ); } #endif } inline void CVertexBuilder::Begin( IVertexBuffer *pVertexBuffer, int nVertexCount ) { Assert( pVertexBuffer && (!m_pVertexBuffer) ); m_pVertexBuffer = pVertexBuffer; m_bModify = false; m_nMaxVertexCount = nVertexCount; m_nVertexCount = 0; // Make sure SetCompressionType was called correctly, if this VB is compressed ValidateCompressionType(); // Lock the vertex and index buffer m_pVertexBuffer->Lock( m_nMaxVertexCount, false, *this ); // Point to the start of the buffers.. Reset(); } //----------------------------------------------------------------------------- // Use this when you're done modifying the mesh //----------------------------------------------------------------------------- inline void CVertexBuilder::End( bool bSpewData ) { // Make sure they called Begin() Assert( !m_bModify ); if ( bSpewData ) { m_pVertexBuffer->Spew( m_nVertexCount, *this ); } #ifdef _DEBUG m_pVertexBuffer->ValidateData( m_nVertexCount, *this ); #endif // Unlock our buffers m_pVertexBuffer->Unlock( m_nVertexCount, *this ); m_pVertexBuffer = 0; m_nMaxVertexCount = 0; m_CompressionType = VERTEX_COMPRESSION_INVALID; #ifdef _DEBUG // Null out our pointers... m_pCurrPosition = NULL; m_pCurrNormal = NULL; m_pCurrColor = NULL; memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); memset( static_cast< VertexDesc_t* >( this ), 0, sizeof(VertexDesc_t) ); #endif } //----------------------------------------------------------------------------- // FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. //----------------------------------------------------------------------------- inline void CVertexBuilder::AttachBegin( IMesh* pMesh, int nMaxVertexCount, const MeshDesc_t &desc ) { VertexCompressionType_t compressionType = m_CompressionType; m_pVertexBuffer = pMesh; memcpy( static_cast( this ), static_cast( &desc ), sizeof(VertexDesc_t) ); m_nMaxVertexCount = nMaxVertexCount; m_NumBoneWeights = m_NumBoneWeights == 0 ? 0 : 2; // Two weights if any m_nVertexCount = 0; m_bModify = false; if ( compressionType != VERTEX_COMPRESSION_INVALID ) m_CompressionType = compressionType; // Make sure SetCompressionType was called correctly, if this VB is compressed ValidateCompressionType(); if ( m_nBufferOffset == INVALID_BUFFER_OFFSET ) { m_nTotalVertexCount = 0; m_nBufferOffset = static_cast< const VertexDesc_t* >( &desc )->m_nOffset; m_nBufferFirstVertex = desc.m_nFirstVertex; } } inline void CVertexBuilder::AttachEnd() { // Make sure they called Begin() Assert( !m_bModify ); m_nMaxVertexCount = 0; m_pVertexBuffer = NULL; m_CompressionType = VERTEX_COMPRESSION_INVALID; #ifdef _DEBUG // Null out our pointers... m_pCurrPosition = NULL; m_pCurrNormal = NULL; m_pCurrColor = NULL; memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); memset( static_cast( this ), 0, sizeof(VertexDesc_t) ); #endif } inline void CVertexBuilder::AttachBeginModify( IMesh* pMesh, int nFirstVertex, int nVertexCount, const MeshDesc_t &desc ) { Assert( pMesh && (!m_pVertexBuffer) ); m_pVertexBuffer = pMesh; memcpy( static_cast( this ), static_cast( &desc ), sizeof(VertexDesc_t) ); m_nMaxVertexCount = m_nVertexCount = nVertexCount; m_NumBoneWeights = m_NumBoneWeights == 0 ? 0 : 2; // Two weights if any m_bModify = true; // Make sure SetCompressionType was called correctly, if this VB is compressed ValidateCompressionType(); } inline void CVertexBuilder::AttachEndModify() { Assert( m_pVertexBuffer ); Assert( m_bModify ); // Make sure they called BeginModify. m_pVertexBuffer = 0; m_nMaxVertexCount = 0; m_CompressionType = VERTEX_COMPRESSION_INVALID; #ifdef _DEBUG // Null out our pointers... m_pCurrPosition = NULL; m_pCurrNormal = NULL; m_pCurrColor = NULL; memset( m_pCurrTexCoord, 0, sizeof( m_pCurrTexCoord ) ); memset( static_cast( this ), 0, sizeof(VertexDesc_t) ); #endif } //----------------------------------------------------------------------------- // Computes the first min non-null address //----------------------------------------------------------------------------- inline unsigned char* FindMinAddress( void *pAddress1, void *pAddress2, int nAddress2Size ) { if ( nAddress2Size == 0 ) return (unsigned char*)pAddress1; if ( !pAddress1 ) return (unsigned char*)pAddress2; return ( pAddress1 < pAddress2 ) ? (unsigned char*)pAddress1 : (unsigned char*)pAddress2; } //----------------------------------------------------------------------------- // Resets the vertex buffer builder so it points to the start of everything again //----------------------------------------------------------------------------- inline void CVertexBuilder::Reset() { m_nCurrentVertex = 0; m_pCurrPosition = m_pPosition; m_pCurrNormal = m_pNormal; for ( int i = 0; i < NELEMS( m_pCurrTexCoord ); i++ ) { m_pCurrTexCoord[i] = m_pTexCoord[i]; } m_pCurrColor = m_pColor; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif #ifdef DEBUG_WRITE_COMBINE // Logic for m_pLastWrittenAddress is tricky. It really wants the min of the // non-null address pointers. m_nLastWrittenField = MB_FIELD_NONE; m_pLastWrittenAddress = NULL; m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pPosition, m_VertexSize_Position ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pBoneWeight, m_VertexSize_BoneWeight ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pBoneMatrixIndex, m_VertexSize_BoneMatrixIndex ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pNormal, m_VertexSize_Normal ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pColor, m_VertexSize_Color ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pSpecular, m_VertexSize_Specular ); for ( int i = 0; i < VERTEX_MAX_TEXTURE_COORDINATES; ++i ) { m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pTexCoord[i], m_VertexSize_TexCoord[i] ); } m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pTangentS, m_VertexSize_TangentS ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pTangentT, m_VertexSize_TangentT ); m_pLastWrittenAddress = FindMinAddress( m_pLastWrittenAddress, m_pUserData, m_VertexSize_UserData ); #endif } //----------------------------------------------------------------------------- // returns the number of vertices //----------------------------------------------------------------------------- inline int CVertexBuilder::VertexCount() const { return m_nVertexCount; } //----------------------------------------------------------------------------- // Returns the total number of vertices across all Locks() //----------------------------------------------------------------------------- inline int CVertexBuilder::TotalVertexCount() const { return m_nTotalVertexCount; } //----------------------------------------------------------------------------- // Returns the base vertex memory pointer //----------------------------------------------------------------------------- inline void* CVertexBuilder::BaseVertexData() { // FIXME: If there's no position specified, we need to find // the base address Assert( m_pPosition ); return m_pPosition; } //----------------------------------------------------------------------------- // Selects the current vertex //----------------------------------------------------------------------------- inline void CVertexBuilder::SelectVertex( int nIndex ) { // NOTE: This index is expected to be relative Assert( (nIndex >= 0) && (nIndex < m_nMaxVertexCount) ); m_nCurrentVertex = nIndex; m_pCurrPosition = OffsetFloatPointer( m_pPosition, m_nCurrentVertex, m_VertexSize_Position ); m_pCurrNormal = OffsetFloatPointer( m_pNormal, m_nCurrentVertex, m_VertexSize_Normal ); COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); m_pCurrTexCoord[0] = OffsetFloatPointer( m_pTexCoord[0], m_nCurrentVertex, m_VertexSize_TexCoord[0] ); m_pCurrTexCoord[1] = OffsetFloatPointer( m_pTexCoord[1], m_nCurrentVertex, m_VertexSize_TexCoord[1] ); m_pCurrTexCoord[2] = OffsetFloatPointer( m_pTexCoord[2], m_nCurrentVertex, m_VertexSize_TexCoord[2] ); m_pCurrTexCoord[3] = OffsetFloatPointer( m_pTexCoord[3], m_nCurrentVertex, m_VertexSize_TexCoord[3] ); m_pCurrTexCoord[4] = OffsetFloatPointer( m_pTexCoord[4], m_nCurrentVertex, m_VertexSize_TexCoord[4] ); m_pCurrTexCoord[5] = OffsetFloatPointer( m_pTexCoord[5], m_nCurrentVertex, m_VertexSize_TexCoord[5] ); m_pCurrTexCoord[6] = OffsetFloatPointer( m_pTexCoord[6], m_nCurrentVertex, m_VertexSize_TexCoord[6] ); m_pCurrTexCoord[7] = OffsetFloatPointer( m_pTexCoord[7], m_nCurrentVertex, m_VertexSize_TexCoord[7] ); m_pCurrColor = m_pColor + m_nCurrentVertex * m_VertexSize_Color; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } //----------------------------------------------------------------------------- // Advances vertex after you're done writing to it. //----------------------------------------------------------------------------- template FORCEINLINE void CVertexBuilder::AdvanceVertexF() { if ( ++m_nCurrentVertex > m_nVertexCount ) { m_nVertexCount = m_nCurrentVertex; } if ( nFlags & VTX_HAVEPOS ) IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); if ( nFlags & VTX_HAVENORMAL ) IncrementFloatPointer( m_pCurrNormal, m_VertexSize_Normal ); if ( nFlags & VTX_HAVECOLOR ) m_pCurrColor += m_VertexSize_Color; COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); if ( nNumTexCoords > 0 ) IncrementFloatPointer( m_pCurrTexCoord[0], m_VertexSize_TexCoord[0] ); if ( nNumTexCoords > 1 ) IncrementFloatPointer( m_pCurrTexCoord[1], m_VertexSize_TexCoord[1] ); if ( nNumTexCoords > 2 ) IncrementFloatPointer( m_pCurrTexCoord[2], m_VertexSize_TexCoord[2] ); if ( nNumTexCoords > 3 ) IncrementFloatPointer( m_pCurrTexCoord[3], m_VertexSize_TexCoord[3] ); if ( nNumTexCoords > 4 ) IncrementFloatPointer( m_pCurrTexCoord[4], m_VertexSize_TexCoord[4] ); if ( nNumTexCoords > 5 ) IncrementFloatPointer( m_pCurrTexCoord[5], m_VertexSize_TexCoord[5] ); if ( nNumTexCoords > 6 ) IncrementFloatPointer( m_pCurrTexCoord[6], m_VertexSize_TexCoord[6] ); if ( nNumTexCoords > 7 ) IncrementFloatPointer( m_pCurrTexCoord[7], m_VertexSize_TexCoord[7] ); #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } inline void CVertexBuilder::AdvanceVertex() { AdvanceVertexF(); } inline void CVertexBuilder::AdvanceVertices( int nVerts ) { m_nCurrentVertex += nVerts; if ( m_nCurrentVertex > m_nVertexCount ) { m_nVertexCount = m_nCurrentVertex; } IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position*nVerts ); IncrementFloatPointer( m_pCurrNormal, m_VertexSize_Normal*nVerts ); COMPILE_TIME_ASSERT( VERTEX_MAX_TEXTURE_COORDINATES == 8 ); IncrementFloatPointer( m_pCurrTexCoord[0], m_VertexSize_TexCoord[0]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[1], m_VertexSize_TexCoord[1]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[2], m_VertexSize_TexCoord[2]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[3], m_VertexSize_TexCoord[3]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[4], m_VertexSize_TexCoord[4]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[5], m_VertexSize_TexCoord[5]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[6], m_VertexSize_TexCoord[6]*nVerts ); IncrementFloatPointer( m_pCurrTexCoord[7], m_VertexSize_TexCoord[7]*nVerts ); m_pCurrColor += m_VertexSize_Color*nVerts; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } //----------------------------------------------------------------------------- // For use with the FastVertex methods, advances the current vertex by N //----------------------------------------------------------------------------- inline void CVertexBuilder::FastAdvanceNVertices( int n ) { m_nCurrentVertex += n; m_nVertexCount = m_nCurrentVertex; } #ifndef COMPILER_MSVC64 // Implement for 64-bit Windows if needed. //----------------------------------------------------------------------------- // Fast Vertex! No need to call advance vertex, and no random access allowed //----------------------------------------------------------------------------- inline void CVertexBuilder::FastVertex( const ModelVertexDX7_t &vertex ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed Assert( m_nCurrentVertex < m_nMaxVertexCount ); #if defined( _WIN32 ) && !defined( _X360 ) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm { mov esi, pRead mov edi, pCurrPos movq mm0, [esi + 0] movq mm1, [esi + 8] movq mm2, [esi + 16] movq mm3, [esi + 24] movq mm4, [esi + 32] movq mm5, [esi + 40] movntq [edi + 0], mm0 movntq [edi + 8], mm1 movntq [edi + 16], mm2 movntq [edi + 24], mm3 movntq [edi + 32], mm4 movntq [edi + 40], mm5 emms } #elif defined(GNUC) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm__ __volatile__ ( "movq (%0), %%mm0\n" "movq 8(%0), %%mm1\n" "movq 16(%0), %%mm2\n" "movq 24(%0), %%mm3\n" "movq 32(%0), %%mm4\n" "movq 40(%0), %%mm5\n" "movntq %%mm0, (%1)\n" "movntq %%mm1, 8(%1)\n" "movntq %%mm2, 16(%1)\n" "movntq %%mm3, 24(%1)\n" "movntq %%mm4, 32(%1)\n" "movntq %%mm5, 40(%1)\n" "emms\n" :: "r" (pRead), "r" (pCurrPos) : "memory"); #else Error( "Implement CMeshBuilder::FastVertex(dx7) "); #endif IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); //m_nVertexCount = ++m_nCurrentVertex; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } inline void CVertexBuilder::FastVertexSSE( const ModelVertexDX7_t &vertex ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed Assert( m_nCurrentVertex < m_nMaxVertexCount ); #if defined( _WIN32 ) && !defined( _X360 ) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm { mov esi, pRead mov edi, pCurrPos movaps xmm0, [esi + 0] movaps xmm1, [esi + 16] movaps xmm2, [esi + 32] movntps [edi + 0], xmm0 movntps [edi + 16], xmm1 movntps [edi + 32], xmm2 } #elif defined(GNUC) const char *pRead = (char *)&vertex; char *pCurrPos = (char *)m_pCurrPosition; __m128 m1 = _mm_load_ps( (float *)pRead ); __m128 m2 = _mm_load_ps( (float *)(pRead + 16) ); __m128 m3 = _mm_load_ps( (float *)(pRead + 32) ); _mm_stream_ps( (float *)pCurrPos, m1 ); _mm_stream_ps( (float *)(pCurrPos + 16), m2 ); _mm_stream_ps( (float *)(pCurrPos + 32), m3 ); #else Error( "Implement CMeshBuilder::FastVertexSSE(dx7)" ); #endif IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); //m_nVertexCount = ++m_nCurrentVertex; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } inline void CVertexBuilder::Fast4VerticesSSE( ModelVertexDX7_t const *vtx_a, ModelVertexDX7_t const *vtx_b, ModelVertexDX7_t const *vtx_c, ModelVertexDX7_t const *vtx_d) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed Assert( m_nCurrentVertex < m_nMaxVertexCount-3 ); #if defined( _WIN32 ) && !defined( _X360 ) void *pCurrPos = m_pCurrPosition; __asm { mov esi, vtx_a mov ecx, vtx_b mov edi, pCurrPos nop movaps xmm0, [esi + 0] movaps xmm1, [esi + 16] movaps xmm2, [esi + 32] movaps xmm3, [ecx + 0] movaps xmm4, [ecx + 16] movaps xmm5, [ecx + 32] mov esi, vtx_c mov ecx, vtx_d movntps [edi + 0], xmm0 movntps [edi + 16], xmm1 movntps [edi + 32], xmm2 movntps [edi + 48], xmm3 movntps [edi + 64], xmm4 movntps [edi + 80], xmm5 movaps xmm0, [esi + 0] movaps xmm1, [esi + 16] movaps xmm2, [esi + 32] movaps xmm3, [ecx + 0] movaps xmm4, [ecx + 16] movaps xmm5, [ecx + 32] movntps [edi + 0+96], xmm0 movntps [edi + 16+96], xmm1 movntps [edi + 32+96], xmm2 movntps [edi + 48+96], xmm3 movntps [edi + 64+96], xmm4 movntps [edi + 80+96], xmm5 } #else Error( "Implement CMeshBuilder::Fast4VerticesSSE\n"); #endif IncrementFloatPointer( m_pCurrPosition, 4*m_VertexSize_Position ); #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } inline void CVertexBuilder::FastVertex( const ModelVertexDX8_t &vertex ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed Assert( m_nCurrentVertex < m_nMaxVertexCount ); #if defined( _WIN32 ) && !defined( _X360 ) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm { mov esi, pRead mov edi, pCurrPos movq mm0, [esi + 0] movq mm1, [esi + 8] movq mm2, [esi + 16] movq mm3, [esi + 24] movq mm4, [esi + 32] movq mm5, [esi + 40] movq mm6, [esi + 48] movq mm7, [esi + 56] movntq [edi + 0], mm0 movntq [edi + 8], mm1 movntq [edi + 16], mm2 movntq [edi + 24], mm3 movntq [edi + 32], mm4 movntq [edi + 40], mm5 movntq [edi + 48], mm6 movntq [edi + 56], mm7 emms } #elif defined(GNUC) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm__ __volatile__ ( "movq (%0), %%mm0\n" "movq 8(%0), %%mm1\n" "movq 16(%0), %%mm2\n" "movq 24(%0), %%mm3\n" "movq 32(%0), %%mm4\n" "movq 40(%0), %%mm5\n" "movq 48(%0), %%mm6\n" "movq 56(%0), %%mm7\n" "movntq %%mm0, (%1)\n" "movntq %%mm1, 8(%1)\n" "movntq %%mm2, 16(%1)\n" "movntq %%mm3, 24(%1)\n" "movntq %%mm4, 32(%1)\n" "movntq %%mm5, 40(%1)\n" "movntq %%mm6, 48(%1)\n" "movntq %%mm7, 56(%1)\n" "emms\n" :: "r" (pRead), "r" (pCurrPos) : "memory"); #else Error( "Implement CMeshBuilder::FastVertex(dx8)" ); #endif IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); // m_nVertexCount = ++m_nCurrentVertex; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } inline void CVertexBuilder::FastVertexSSE( const ModelVertexDX8_t &vertex ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed Assert( m_nCurrentVertex < m_nMaxVertexCount ); #if defined( _WIN32 ) && !defined( _X360 ) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm { mov esi, pRead mov edi, pCurrPos movaps xmm0, [esi + 0] movaps xmm1, [esi + 16] movaps xmm2, [esi + 32] movaps xmm3, [esi + 48] movntps [edi + 0], xmm0 movntps [edi + 16], xmm1 movntps [edi + 32], xmm2 movntps [edi + 48], xmm3 } #elif defined(GNUC) const void *pRead = &vertex; void *pCurrPos = m_pCurrPosition; __asm__ __volatile__ ( "movaps (%0), %%xmm0\n" "movaps 16(%0), %%xmm1\n" "movaps 32(%0), %%xmm2\n" "movaps 48(%0), %%xmm3\n" "movntps %%xmm0, (%1)\n" "movntps %%xmm1, 16(%1)\n" "movntps %%xmm2, 32(%1)\n" "movntps %%xmm3, 48(%1)\n" :: "r" (pRead), "r" (pCurrPos) : "memory"); #else Error( "Implement CMeshBuilder::FastVertexSSE((dx8)" ); #endif IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); // m_nVertexCount = ++m_nCurrentVertex; #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } #endif // COMPILER_MSVC64 //----------------------------------------------------------------------------- // Returns the current vertex //----------------------------------------------------------------------------- inline int CVertexBuilder::GetCurrentVertex() const { return m_nCurrentVertex; } //----------------------------------------------------------------------------- // Copies a vertex into the x360 format //----------------------------------------------------------------------------- #if defined( _X360 ) inline void CVertexBuilder::VertexDX8ToX360( const ModelVertexDX8_t &vertex ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // FIXME: support compressed verts if needed Assert( m_nCurrentVertex < m_nMaxVertexCount ); // get the start of the data unsigned char *pDst = (unsigned char*)m_pCurrPosition; Assert( m_VertexSize_Position > 0 ); // Assume position is always present Assert( GetVertexElementSize( VERTEX_ELEMENT_POSITION, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecPosition ) ); memcpy( pDst, vertex.m_vecPosition.Base(), sizeof( vertex.m_vecPosition ) ); pDst += sizeof( vertex.m_vecPosition ); if ( m_VertexSize_BoneWeight ) { Assert( vertex.m_flBoneWeights[0] >= 0 && vertex.m_flBoneWeights[0] <= 1.0f ); Assert( vertex.m_flBoneWeights[1] >= 0 && vertex.m_flBoneWeights[1] <= 1.0f ); Assert( GetVertexElementSize( VERTEX_ELEMENT_BONEWEIGHTS2, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_flBoneWeights ) ); memcpy( pDst, vertex.m_flBoneWeights.Base(), sizeof( vertex.m_flBoneWeights ) ); pDst += sizeof( vertex.m_flBoneWeights ); if ( m_VertexSize_BoneMatrixIndex ) { Assert( GetVertexElementSize( VERTEX_ELEMENT_BONEINDEX, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_nBoneIndices ) ); *(unsigned int*)pDst = vertex.m_nBoneIndices; pDst += sizeof( vertex.m_nBoneIndices ); } } if ( m_VertexSize_Normal ) { Assert( GetVertexElementSize( VERTEX_ELEMENT_NORMAL, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecNormal ) ); memcpy( pDst, vertex.m_vecNormal.Base(), sizeof( vertex.m_vecNormal ) ); pDst += sizeof( vertex.m_vecNormal ); } if ( m_VertexSize_Color ) { Assert( GetVertexElementSize( VERTEX_ELEMENT_COLOR, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_nColor ) ); *(unsigned int*)pDst = vertex.m_nColor; pDst += sizeof( vertex.m_nColor ); } if ( m_VertexSize_TexCoord[0] ) { Assert( GetVertexElementSize( VERTEX_ELEMENT_TEXCOORD2D_0, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecTexCoord ) ); memcpy( pDst, vertex.m_vecTexCoord.Base(), sizeof( vertex.m_vecTexCoord ) ); pDst += sizeof( vertex.m_vecTexCoord ); } if ( m_VertexSize_UserData ) { Assert( GetVertexElementSize( VERTEX_ELEMENT_USERDATA4, VERTEX_COMPRESSION_NONE ) == sizeof( vertex.m_vecUserData ) ); memcpy( pDst, vertex.m_vecUserData.Base(), sizeof( vertex.m_vecUserData ) ); pDst += sizeof( vertex.m_vecUserData ); } // ensure code is synced with the mesh builder that established the offsets Assert( pDst - (unsigned char*)m_pCurrPosition == m_VertexSize_Position ); IncrementFloatPointer( m_pCurrPosition, m_VertexSize_Position ); #if ( defined( _DEBUG ) && ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) ) m_bWrittenNormal = false; m_bWrittenUserData = false; #endif } #endif //----------------------------------------------------------------------------- // Data retrieval... //----------------------------------------------------------------------------- inline const float* CVertexBuilder::Position() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return m_pCurrPosition; } inline const float* CVertexBuilder::Normal() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return m_pCurrNormal; } inline unsigned int CVertexBuilder::Color() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Swizzle it so it returns the same format as accepted by Color4ubv - rgba Assert( m_nCurrentVertex < m_nMaxVertexCount ); unsigned int color; if ( IsPC() || !IsX360() ) { color = (m_pCurrColor[3] << 24) | (m_pCurrColor[0] << 16) | (m_pCurrColor[1] << 8) | (m_pCurrColor[2]); } else { // in memory as argb, back to rgba color = (m_pCurrColor[1] << 24) | (m_pCurrColor[2] << 16) | (m_pCurrColor[3] << 8) | (m_pCurrColor[0]); } return color; } inline unsigned char *CVertexBuilder::Specular() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return m_pSpecular + m_nCurrentVertex * m_VertexSize_Specular; } inline const float* CVertexBuilder::TexCoord( int stage ) const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return m_pCurrTexCoord[stage]; } inline const float* CVertexBuilder::TangentS() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return OffsetFloatPointer( m_pTangentS, m_nCurrentVertex, m_VertexSize_TangentS ); } inline const float* CVertexBuilder::TangentT() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return OffsetFloatPointer( m_pTangentT, m_nCurrentVertex, m_VertexSize_TangentT ); } inline float CVertexBuilder::Wrinkle() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return *OffsetFloatPointer( m_pWrinkle, m_nCurrentVertex, m_VertexSize_Wrinkle ); } inline const float* CVertexBuilder::BoneWeight() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); } inline int CVertexBuilder::NumBoneWeights() const { return m_NumBoneWeights; } #ifndef NEW_SKINNING inline unsigned char* CVertexBuilder::BoneMatrix() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return m_pBoneMatrixIndex + m_nCurrentVertex * m_VertexSize_BoneMatrixIndex; } #else inline float* CVertexBuilder::BoneMatrix() const { // FIXME: add a templatized accessor (return type varies to ensure calling code is updated appropriately) // for code that needs to access compressed data (and/or a return-by-value templatized accessor) Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); Assert( m_nCurrentVertex < m_nMaxVertexCount ); return m_pBoneMatrixIndex + m_nCurrentVertex * m_VertexSize_BoneMatrixIndex; } #endif //----------------------------------------------------------------------------- // Position setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::Position3f( float x, float y, float z ) { Assert( m_pPosition && m_pCurrPosition ); Assert( IsFinite(x) && IsFinite(y) && IsFinite(z) ); float *pDst = m_pCurrPosition; *pDst++ = x; *pDst++ = y; *pDst = z; } inline void CVertexBuilder::Position3fv( const float *v ) { Assert(v); Assert( m_pPosition && m_pCurrPosition ); float *pDst = m_pCurrPosition; *pDst++ = *v++; *pDst++ = *v++; *pDst = *v; } //----------------------------------------------------------------------------- // Normal setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::Normal3f( float nx, float ny, float nz ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression Assert( m_pNormal ); Assert( IsFinite(nx) && IsFinite(ny) && IsFinite(nz) ); Assert( nx >= -1.05f && nx <= 1.05f ); Assert( ny >= -1.05f && ny <= 1.05f ); Assert( nz >= -1.05f && nz <= 1.05f ); float *pDst = m_pCurrNormal; *pDst++ = nx; *pDst++ = ny; *pDst = nz; } inline void CVertexBuilder::Normal3fv( const float *n ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression Assert( n ); Assert( m_pNormal && m_pCurrNormal ); Assert( IsFinite(n[0]) && IsFinite(n[1]) && IsFinite(n[2]) ); Assert( n[0] >= -1.05f && n[0] <= 1.05f ); Assert( n[1] >= -1.05f && n[1] <= 1.05f ); Assert( n[2] >= -1.05f && n[2] <= 1.05f ); float *pDst = m_pCurrNormal; *pDst++ = *n++; *pDst++ = *n++; *pDst = *n; } inline void CVertexBuilder::NormalDelta3f( float nx, float ny, float nz ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression Assert( m_pNormal ); Assert( IsFinite(nx) && IsFinite(ny) && IsFinite(nz) ); float *pDst = m_pCurrNormal; *pDst++ = nx; *pDst++ = ny; *pDst = nz; } inline void CVertexBuilder::NormalDelta3fv( const float *n ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression Assert( n ); Assert( m_pNormal && m_pCurrNormal ); Assert( IsFinite(n[0]) && IsFinite(n[1]) && IsFinite(n[2]) ); float *pDst = m_pCurrNormal; *pDst++ = *n++; *pDst++ = *n++; *pDst = *n; } //----------------------------------------------------------------------------- // Templatized normal setting methods which support compressed vertices //----------------------------------------------------------------------------- template inline void CVertexBuilder::CompressedNormal3f( float nx, float ny, float nz ) { Assert( T == m_CompressionType ); Assert( m_pNormal && m_pCurrNormal ); Assert( IsFinite(nx) && IsFinite(ny) && IsFinite(nz) ); Assert( nx >= -1.05f && nx <= 1.05f ); Assert( ny >= -1.05f && ny <= 1.05f ); Assert( nz >= -1.05f && nz <= 1.05f ); // FIXME: studiorender is passing in non-unit normals //float lengthSqd = nx*nx + ny*ny + nz*nz; //Assert( lengthSqd >= 0.95f && lengthSqd <= 1.05f ); if ( T == VERTEX_COMPRESSION_ON ) { #if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) PackNormal_SHORT2( nx, ny, nz, (unsigned int *)m_pCurrNormal ); #else //( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) // NOTE: write the normal into the lower 16 bits of a word, clearing the top 16 bits - a userdata4 // tangent must be written into the upper 16 bits by CompressedUserData() *AFTER* this. #ifdef _DEBUG Assert( m_bWrittenUserData == false ); m_bWrittenNormal = true; #endif PackNormal_UBYTE4( nx, ny, nz, (unsigned int *)m_pCurrNormal ); #endif } else { float *pDst = m_pCurrNormal; *pDst++ = nx; *pDst++ = ny; *pDst = nz; } } template inline void CVertexBuilder::CompressedNormal3fv( const float *n ) { Assert( n ); CompressedNormal3f( n[0], n[1], n[2] ); } //----------------------------------------------------------------------------- // Color setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::Color3f( float r, float g, float b ) { Assert( m_pColor && m_pCurrColor ); Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) ); Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) ); Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) ); #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(r)) | (FastFToC(g) << 8) | (FastFToC(b) << 16) | 0xFF000000; #else int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | 0xFF000000; #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Color3fv( const float *rgb ) { Assert(rgb); Assert( m_pColor && m_pCurrColor ); Assert( IsFinite(rgb[0]) && IsFinite(rgb[1]) && IsFinite(rgb[2]) ); Assert( (rgb[0] >= 0.0) && (rgb[1] >= 0.0) && (rgb[2] >= 0.0) ); Assert( (rgb[0] <= 1.0) && (rgb[1] <= 1.0) && (rgb[2] <= 1.0) ); #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(rgb[0])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[2]) << 16) | 0xFF000000; #else int col = (FastFToC(rgb[2])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[0]) << 16) | 0xFF000000; #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Color4f( float r, float g, float b, float a ) { Assert( m_pColor && m_pCurrColor ); Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) && IsFinite(a) ); Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) && (a >= 0.0) ); Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) && (a <= 1.0) ); #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(r)) | (FastFToC(g) << 8) | (FastFToC(b) << 16) | (FastFToC(a) << 24); #else int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | (FastFToC(a) << 24); #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Color4fv( const float *rgba ) { Assert(rgba); Assert( m_pColor && m_pCurrColor ); Assert( IsFinite(rgba[0]) && IsFinite(rgba[1]) && IsFinite(rgba[2]) && IsFinite(rgba[3]) ); Assert( (rgba[0] >= 0.0) && (rgba[1] >= 0.0) && (rgba[2] >= 0.0) && (rgba[3] >= 0.0) ); Assert( (rgba[0] <= 1.0) && (rgba[1] <= 1.0) && (rgba[2] <= 1.0) && (rgba[3] <= 1.0) ); #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(rgba[0])) | (FastFToC(rgba[1]) << 8) | (FastFToC(rgba[2]) << 16) | (FastFToC(rgba[3]) << 24); #else int col = (FastFToC(rgba[2])) | (FastFToC(rgba[1]) << 8) | (FastFToC(rgba[0]) << 16) | (FastFToC(rgba[3]) << 24); #endif *(int*)m_pCurrColor = col; } //----------------------------------------------------------------------------- // Faster versions of color //----------------------------------------------------------------------------- // note that on the OSX target (OpenGL) whenever there is vertex data being written as bytes - they need to be written in R,G,B,A memory order inline void CVertexBuilder::Color3ub( unsigned char r, unsigned char g, unsigned char b ) { Assert( m_pColor && m_pCurrColor ); #ifdef OPENGL_SWAP_COLORS int col = r | (g << 8) | (b << 16) | 0xFF000000; // r, g, b, a in memory #else int col = b | (g << 8) | (r << 16) | 0xFF000000; #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Color3ubv( unsigned char const* rgb ) { Assert(rgb); Assert( m_pColor && m_pCurrColor ); #ifdef OPENGL_SWAP_COLORS int col = rgb[0] | (rgb[1] << 8) | (rgb[2] << 16) | 0xFF000000; // r, g, b, a in memory #else int col = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16) | 0xFF000000; #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) { Assert( m_pColor && m_pCurrColor ); #ifdef OPENGL_SWAP_COLORS int col = r | (g << 8) | (b << 16) | (a << 24); // r, g, b, a in memory #else int col = b | (g << 8) | (r << 16) | (a << 24); #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Color4ubv( unsigned char const* rgba ) { Assert( rgba ); Assert( m_pColor && m_pCurrColor ); #ifdef OPENGL_SWAP_COLORS int col = rgba[0] | (rgba[1] << 8) | (rgba[2] << 16) | (rgba[3] << 24); // r, g, b, a in memory #else int col = rgba[2] | (rgba[1] << 8) | (rgba[0] << 16) | (rgba[3] << 24); #endif *(int*)m_pCurrColor = col; } inline void CVertexBuilder::Specular3f( float r, float g, float b ) { Assert( m_pSpecular ); Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) ); Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) ); Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) ); unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(r)) | (FastFToC(g) << 8) | (FastFToC(b) << 16) | 0xFF000000; #else int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | 0xFF000000; #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular3fv( const float *rgb ) { Assert(rgb); Assert( m_pSpecular ); Assert( IsFinite(rgb[0]) && IsFinite(rgb[1]) && IsFinite(rgb[2]) ); Assert( (rgb[0] >= 0.0) && (rgb[1] >= 0.0) && (rgb[2] >= 0.0) ); Assert( (rgb[0] <= 1.0) && (rgb[1] <= 1.0) && (rgb[2] <= 1.0) ); unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(rgb[0])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[2]) << 16) | 0xFF000000; #else int col = (FastFToC(rgb[2])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[0]) << 16) | 0xFF000000; #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular4f( float r, float g, float b, float a ) { Assert( m_pSpecular ); Assert( IsFinite(r) && IsFinite(g) && IsFinite(b) && IsFinite(a) ); Assert( (r >= 0.0) && (g >= 0.0) && (b >= 0.0) && (a >= 0.0) ); Assert( (r <= 1.0) && (g <= 1.0) && (b <= 1.0) && (a <= 1.0f) ); unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(r)) | (FastFToC(g) << 8) | (FastFToC(b) << 16) | (FastFToC(a) << 24); #else int col = (FastFToC(b)) | (FastFToC(g) << 8) | (FastFToC(r) << 16) | (FastFToC(a) << 24); #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular4fv( const float *rgb ) { Assert(rgb); Assert( m_pSpecular ); Assert( IsFinite(rgb[0]) && IsFinite(rgb[1]) && IsFinite(rgb[2]) && IsFinite(rgb[3]) ); Assert( (rgb[0] >= 0.0) && (rgb[1] >= 0.0) && (rgb[2] >= 0.0) && (rgb[3] >= 0.0) ); Assert( (rgb[0] <= 1.0) && (rgb[1] <= 1.0) && (rgb[2] <= 1.0) && (rgb[3] <= 1.0) ); unsigned char* pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = (FastFToC(rgb[0])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[2]) << 16) | (FastFToC(rgb[3]) << 24); #else int col = (FastFToC(rgb[2])) | (FastFToC(rgb[1]) << 8) | (FastFToC(rgb[0]) << 16) | (FastFToC(rgb[3]) << 24); #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular3ub( unsigned char r, unsigned char g, unsigned char b ) { Assert( m_pSpecular ); unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = r | (g << 8) | (b << 16) | 0xFF000000; // r, g, b, a in memory #else int col = b | (g << 8) | (r << 16) | 0xFF000000; #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular3ubv( unsigned char const *c ) { Assert( m_pSpecular ); unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = c[0] | (c[1] << 8) | (c[2] << 16) | 0xFF000000; // r, g, b, a in memory #else int col = c[2] | (c[1] << 8) | (c[0] << 16) | 0xFF000000; #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) { Assert( m_pSpecular ); unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = r | (g << 8) | (b << 16) | (a << 24); // r, g, b, a in memory #else int col = b | (g << 8) | (r << 16) | (a << 24); #endif *(int*)pSpecular = col; } inline void CVertexBuilder::Specular4ubv( unsigned char const *c ) { Assert( m_pSpecular ); unsigned char *pSpecular = &m_pSpecular[m_nCurrentVertex * m_VertexSize_Specular]; #ifdef OPENGL_SWAP_COLORS int col = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); #else int col = c[2] | (c[1] << 8) | (c[0] << 16) | (c[3] << 24); #endif *(int*)pSpecular = col; } //----------------------------------------------------------------------------- // Texture coordinate setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::TexCoord1f( int nStage, float s ) { Assert( m_pTexCoord[nStage] && m_pCurrTexCoord[nStage] ); Assert( IsFinite(s) ); float *pDst = m_pCurrTexCoord[nStage]; *pDst = s; } inline void CVertexBuilder::TexCoord2f( int nStage, float s, float t ) { Assert( m_pTexCoord[nStage] && m_pCurrTexCoord[nStage] ); Assert( IsFinite(s) && IsFinite(t) ); float *pDst = m_pCurrTexCoord[nStage]; *pDst++ = s; *pDst = t; } inline void CVertexBuilder::TexCoord2fv( int nStage, const float *st ) { Assert(st); Assert( m_pTexCoord[nStage] && m_pCurrTexCoord[nStage] ); Assert( IsFinite(st[0]) && IsFinite(st[1]) ); float *pDst = m_pCurrTexCoord[nStage]; *pDst++ = *st++; *pDst = *st; } inline void CVertexBuilder::TexCoord3f( int stage, float s, float t, float u ) { // Tried to add too much! Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); Assert( IsFinite(s) && IsFinite(t) && IsFinite(u) ); float *pDst = m_pCurrTexCoord[stage]; *pDst++ = s; *pDst++ = t; *pDst = u; } inline void CVertexBuilder::TexCoord3fv( int stage, const float *stu ) { Assert(stu); Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); Assert( IsFinite(stu[0]) && IsFinite(stu[1]) && IsFinite(stu[2]) ); float *pDst = m_pCurrTexCoord[stage]; *pDst++ = *stu++; *pDst++ = *stu++; *pDst = *stu; } inline void CVertexBuilder::TexCoord4f( int stage, float s, float t, float u, float v ) { // Tried to add too much! Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); Assert( IsFinite(s) && IsFinite(t) && IsFinite(u) ); float *pDst = m_pCurrTexCoord[stage]; *pDst++ = s; *pDst++ = t; *pDst++ = u; *pDst = v; } inline void CVertexBuilder::TexCoord4fv( int stage, const float *stuv ) { Assert(stuv); Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); Assert( IsFinite(stuv[0]) && IsFinite(stuv[1]) && IsFinite(stuv[2]) ); float *pDst = m_pCurrTexCoord[stage]; *pDst++ = *stuv++; *pDst++ = *stuv++; *pDst++ = *stuv++; *pDst = *stuv; } inline void CVertexBuilder::TexCoordSubRect2f( int stage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ) { Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); Assert( IsFinite(s) && IsFinite(t) ); float *pDst = m_pCurrTexCoord[stage]; *pDst++ = ( s * scaleS ) + offsetS; *pDst = ( t * scaleT ) + offsetT; } inline void CVertexBuilder::TexCoordSubRect2fv( int stage, const float *st, const float *offset, const float *scale ) { Assert(st); Assert( m_pTexCoord[stage] && m_pCurrTexCoord[stage] ); Assert( IsFinite(st[0]) && IsFinite(st[1]) ); float *pDst = m_pCurrTexCoord[stage]; *pDst++ = ( *st++ * *scale++ ) + *offset++; *pDst = ( *st * *scale ) + *offset; } //----------------------------------------------------------------------------- // Tangent space setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::TangentS3f( float sx, float sy, float sz ) { Assert( m_pTangentS ); Assert( IsFinite(sx) && IsFinite(sy) && IsFinite(sz) ); float* pTangentS = OffsetFloatPointer( m_pTangentS, m_nCurrentVertex, m_VertexSize_TangentS ); *pTangentS++ = sx; *pTangentS++ = sy; *pTangentS = sz; } inline void CVertexBuilder::TangentS3fv( const float* s ) { Assert( s ); Assert( m_pTangentS ); Assert( IsFinite(s[0]) && IsFinite(s[1]) && IsFinite(s[2]) ); float* pTangentS = OffsetFloatPointer( m_pTangentS, m_nCurrentVertex, m_VertexSize_TangentS ); *pTangentS++ = *s++; *pTangentS++ = *s++; *pTangentS = *s; } inline void CVertexBuilder::TangentT3f( float tx, float ty, float tz ) { Assert( m_pTangentT ); Assert( IsFinite(tx) && IsFinite(ty) && IsFinite(tz) ); float* pTangentT = OffsetFloatPointer( m_pTangentT, m_nCurrentVertex, m_VertexSize_TangentT ); *pTangentT++ = tx; *pTangentT++ = ty; *pTangentT = tz; } inline void CVertexBuilder::TangentT3fv( const float* t ) { Assert( t ); Assert( m_pTangentT ); Assert( IsFinite(t[0]) && IsFinite(t[1]) && IsFinite(t[2]) ); float* pTangentT = OffsetFloatPointer( m_pTangentT, m_nCurrentVertex, m_VertexSize_TangentT ); *pTangentT++ = *t++; *pTangentT++ = *t++; *pTangentT = *t; } //----------------------------------------------------------------------------- // Wrinkle setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::Wrinkle1f( float flWrinkle ) { Assert( m_pWrinkle ); Assert( IsFinite(flWrinkle) ); float *pWrinkle = OffsetFloatPointer( m_pWrinkle, m_nCurrentVertex, m_VertexSize_Wrinkle ); *pWrinkle = flWrinkle; } //----------------------------------------------------------------------------- // Bone weight setting methods //----------------------------------------------------------------------------- inline void CVertexBuilder::BoneWeight( int idx, float weight ) { Assert( m_pBoneWeight ); Assert( IsFinite( weight ) ); Assert( idx >= 0 ); AssertOnce( m_NumBoneWeights == 2 ); // This test is here because we store N-1 bone weights (the Nth is computed in // the vertex shader as "1 - C", where C is the sum of the (N-1) other weights) if ( idx < m_NumBoneWeights ) { float* pBoneWeight = OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); pBoneWeight[idx] = weight; } } static int sg_IndexSwap[4] = { 2, 1, 0, 3 }; inline void CVertexBuilder::BoneMatrix( int idx, int matrixIdx ) { Assert( m_pBoneMatrixIndex ); Assert( idx >= 0 ); Assert( idx < 4 ); // garymcthack if ( matrixIdx == BONE_MATRIX_INDEX_INVALID ) { matrixIdx = 0; } Assert( (matrixIdx >= 0) && (matrixIdx < 53) ); #ifdef OPENGL_SWAP_COLORS idx = sg_IndexSwap[idx]; #endif #ifndef NEW_SKINNING unsigned char* pBoneMatrix = &m_pBoneMatrixIndex[m_nCurrentVertex * m_VertexSize_BoneMatrixIndex]; if ( IsX360() ) { // store sequentially as wzyx order, gpu delivers as xyzw idx = 3-idx; } pBoneMatrix[idx] = (unsigned char)matrixIdx; #else float* pBoneMatrix = &m_pBoneMatrixIndex[m_nCurrentVertex * m_VertexSize_BoneMatrixIndex]; pBoneMatrix[idx] = matrixIdx; #endif } //----------------------------------------------------------------------------- // Templatized bone weight setting methods which support compressed vertices //----------------------------------------------------------------------------- template inline void CVertexBuilder::CompressedBoneWeight3fv( const float * pWeights ) { Assert( T == m_CompressionType ); Assert( m_pBoneWeight ); Assert( pWeights ); float *pDestWeights = OffsetFloatPointer( m_pBoneWeight, m_nCurrentVertex, m_VertexSize_BoneWeight ); if ( T == VERTEX_COMPRESSION_ON ) { // Quantize to 15 bits per weight (we use D3DDECLTYPE_SHORT2) // NOTE: we perform careful normalization (weights sum to 1.0f in the vertex shader), so // as to avoid cracking at boundaries between meshes with different numbers of weights // per vertex. For example, (1) needs to yield the same normalized weights as (1,0), // and (0.5,0.49) needs to normalize the same normalized weights as (0.5,0.49,0). // The key is that values which are *computed* in the shader (e.g. the second weight // in a 2-weight mesh) must exactly equal values which are *read* from the vertex // stream (e.g. the second weight in a 3-weight mesh). // Only 1 or 2 weights (SHORT2N) supported for compressed verts so far Assert( m_NumBoneWeights <= 2 ); const int WEIGHT0_SHIFT = IsX360() ? 16 : 0; const int WEIGHT1_SHIFT = IsX360() ? 0 : 16; unsigned int *weights = (unsigned int *)pDestWeights; // We scale our weights so that they sum to 32768, then subtract 1 (which gets added // back in the shader), because dividing by 32767 introduces nasty rounding issues. Assert( IsFinite( pWeights[0] ) && ( pWeights[0] >= 0.0f ) && ( pWeights[0] <= 1.0f ) ); unsigned int weight0 = Float2Int( pWeights[0] * 32768.0f ); *weights = ( 0x0000FFFF & (weight0 - 1) ) << WEIGHT0_SHIFT; #ifdef DEBUG if ( m_NumBoneWeights == 1 ) { // Double-check the validity of the values that were passed in Assert( IsFinite( pWeights[1] ) && ( pWeights[1] >= 0.0f ) && ( pWeights[1] <= 1.0f ) ); unsigned int weight1 = Float2Int( pWeights[1] * 32768.0f ); Assert( ( weight0 + weight1 ) <= 32768 ); } #endif if ( m_NumBoneWeights > 1 ) { // This path for 3 weights per vert (2 are stored and the 3rd is computed // in the shader - we do post-quantization normalization here in such a // way as to avoid mesh-boundary cracking) Assert( m_NumBoneWeights == 2 ); Assert( IsFinite( pWeights[1] ) && ( pWeights[1] >= 0.0f ) && ( pWeights[1] <= 1.0f ) ); Assert( IsFinite( pWeights[2] ) && ( pWeights[2] >= 0.0f ) && ( pWeights[2] <= 1.0f ) ); unsigned int weight1 = Float2Int( pWeights[1] * 32768.0f ); unsigned int weight2 = Float2Int( pWeights[2] * 32768.0f ); Assert( ( weight0 + weight1 + weight2 ) <= 32768 ); unsigned int residual = 32768 - ( weight0 + weight1 + weight2 ); weight1 += residual; // Normalize *weights |= ( 0x0000FFFF & ( weight1 - 1 ) ) << WEIGHT1_SHIFT; } } else // Uncompressed path { pDestWeights[0] = pWeights[0]; pDestWeights[1] = pWeights[1]; } } //----------------------------------------------------------------------------- // Generic per-vertex data setting method //----------------------------------------------------------------------------- inline void CVertexBuilder::UserData( const float* pData ) { Assert( m_CompressionType == VERTEX_COMPRESSION_NONE ); // Use the templatized version if you want to support compression Assert( pData ); int userDataSize = 4; // garymcthack float *pUserData = OffsetFloatPointer( m_pUserData, m_nCurrentVertex, m_VertexSize_UserData ); memcpy( pUserData, pData, sizeof( float ) * userDataSize ); } //----------------------------------------------------------------------------- // Templatized generic per-vertex data setting method which supports compressed vertices //----------------------------------------------------------------------------- template inline void CVertexBuilder::CompressedUserData( const float* pData ) { Assert( T == m_CompressionType ); Assert( pData ); // This is always in fact a tangent vector, not generic 'userdata' Assert( IsFinite(pData[0]) && IsFinite(pData[1]) && IsFinite(pData[2]) ); Assert( pData[0] >= -1.05f && pData[0] <= 1.05f ); Assert( pData[1] >= -1.05f && pData[1] <= 1.05f ); Assert( pData[2] >= -1.05f && pData[2] <= 1.05f ); Assert( pData[3] == +1.0f || pData[3] == -1.0f ); // FIXME: studiorender is passing in non-unit normals //float lengthSqd = pData[0]*pData[0] + pData[1]*pData[1] + pData[2]*pData[2]; //Assert( lengthSqd >= 0.95f && lengthSqd <= 1.05f ); if ( T == VERTEX_COMPRESSION_ON ) { float binormalSign = pData[3]; #if ( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_SEPARATETANGENTS_SHORT2 ) float *pUserData = OffsetFloatPointer( m_pUserData, m_nCurrentVertex, m_VertexSize_UserData ); PackNormal_SHORT2( pData, (unsigned int *)pUserData, binormalSign ); #else //( COMPRESSED_NORMALS_TYPE == COMPRESSED_NORMALS_COMBINEDTANGENTS_UBYTE4 ) // FIXME: add a combined CompressedNormalAndTangent() accessor, to avoid reading back from write-combined memory here // The normal should have already been written into the lower 16 // bits - here, we OR in the tangent into the upper 16 bits unsigned int existingNormalData = *(unsigned int *)m_pCurrNormal; Assert( ( existingNormalData & 0xFFFF0000 ) == 0 ); #ifdef _DEBUG Assert( m_bWrittenNormal == true ); m_bWrittenUserData = true; #endif bool bIsTangent = true; unsigned int tangentData = 0; PackNormal_UBYTE4( pData, &tangentData, bIsTangent, binormalSign ); *(unsigned int *)m_pCurrNormal = existingNormalData | tangentData; #endif } else { int userDataSize = 4; // garymcthack float *pUserData = OffsetFloatPointer( m_pUserData, m_nCurrentVertex, m_VertexSize_UserData ); memcpy( pUserData, pData, sizeof( float ) * userDataSize ); } } //----------------------------------------------------------------------------- // // Helper class used to define index buffers // //----------------------------------------------------------------------------- class CIndexBuilder : private IndexDesc_t { public: CIndexBuilder(); CIndexBuilder( IIndexBuffer *pIndexBuffer, MaterialIndexFormat_t fmt = MATERIAL_INDEX_FORMAT_UNKNOWN ); ~CIndexBuilder(); // Begins, ends modification of the index buffer (returns true if the lock succeeded) // A lock may not succeed if append is set to true and there isn't enough room // NOTE: Append is only used with dynamic index buffers; it's ignored for static buffers bool Lock( int nMaxIndexCount, int nIndexOffset, bool bAppend = false ); void Unlock(); // Spews the current data // NOTE: Can only be called during a lock/unlock block void SpewData(); // Returns the number of indices we can fit into the buffer without needing to discard int GetRoomRemaining() const; // Binds this index buffer void Bind( IMatRenderContext *pContext ); // Returns the byte offset int Offset() const; // Begins, ends modification of the index buffer // NOTE: IndexOffset is the number to add to all indices written into the buffer; // useful when using dynamic vertex buffers. void Begin( IIndexBuffer *pIndexBuffer, int nMaxIndexCount, int nIndexOffset = 0 ); void End( bool bSpewData = false ); // Locks the index buffer to modify existing data // Passing nVertexCount == -1 says to lock all the vertices for modification. // Pass 0 for nIndexCount to not lock the index buffer. void BeginModify( IIndexBuffer *pIndexBuffer, int nFirstIndex = 0, int nIndexCount = 0, int nIndexOffset = 0 ); void EndModify( bool bSpewData = false ); // returns the number of indices int IndexCount() const; // Returns the total number of indices across all Locks() int TotalIndexCount() const; // Resets the mesh builder so it points to the start of everything again void Reset(); // Selects the nth Index void SelectIndex( int nBufferIndex ); // Advances the current index by one void AdvanceIndex(); void AdvanceIndices( int nIndexCount ); int GetCurrentIndex(); int GetFirstIndex() const; unsigned short const* Index() const; // Used to define the indices (only used if you aren't using primitives) void Index( unsigned short nIndex ); // Fast Index! No need to call advance index, and no random access allowed void FastIndex( unsigned short nIndex ); // NOTE: This version is the one you really want to achieve write-combining; // Write combining only works if you write in 4 bytes chunks. void FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ); // Generates indices for a particular primitive type void GenerateIndices( MaterialPrimitiveType_t primitiveType, int nIndexCount ); // FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. void AttachBegin( IMesh* pMesh, int nMaxIndexCount, const MeshDesc_t &desc ); void AttachEnd(); void AttachBeginModify( IMesh* pMesh, int nFirstIndex, int nIndexCount, const MeshDesc_t &desc ); void AttachEndModify(); void FastTriangle( int startVert ); void FastQuad( int startVert ); void FastPolygon( int startVert, int numTriangles ); void FastPolygonList( int startVert, int *pVertexCount, int polygonCount ); void FastIndexList( const unsigned short *pIndexList, int startVert, int indexCount ); private: // The mesh we're modifying IIndexBuffer *m_pIndexBuffer; // Max number of indices int m_nMaxIndexCount; // Number of indices int m_nIndexCount; // Offset to add to each index as it's written into the buffer int m_nIndexOffset; // The current index mutable int m_nCurrentIndex; // Total number of indices appended int m_nTotalIndexCount; // First index buffer offset + first index unsigned int m_nBufferOffset; unsigned int m_nBufferFirstIndex; // Used to make sure Begin/End calls and BeginModify/EndModify calls match. bool m_bModify; }; //----------------------------------------------------------------------------- // // Inline methods related to CIndexBuilder // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- inline CIndexBuilder::CIndexBuilder() : m_pIndexBuffer(0), m_nIndexCount(0), m_nCurrentIndex(0), m_nMaxIndexCount(0) { m_nTotalIndexCount = 0; m_nBufferOffset = INVALID_BUFFER_OFFSET; m_nBufferFirstIndex = 0; #ifdef _DEBUG m_bModify = false; #endif } inline CIndexBuilder::CIndexBuilder( IIndexBuffer *pIndexBuffer, MaterialIndexFormat_t fmt ) { m_pIndexBuffer = pIndexBuffer; m_nBufferOffset = INVALID_BUFFER_OFFSET; m_nBufferFirstIndex = 0; m_nIndexCount = 0; m_nCurrentIndex = 0; m_nMaxIndexCount = 0; m_nTotalIndexCount = 0; if ( m_pIndexBuffer->IsDynamic() ) { m_pIndexBuffer->BeginCastBuffer( fmt ); } else { Assert( m_pIndexBuffer->IndexFormat() == fmt ); } #ifdef _DEBUG m_bModify = false; #endif } inline CIndexBuilder::~CIndexBuilder() { if ( m_pIndexBuffer && m_pIndexBuffer->IsDynamic() ) { m_pIndexBuffer->EndCastBuffer(); } } //----------------------------------------------------------------------------- // Begins, ends modification of the index buffer //----------------------------------------------------------------------------- inline bool CIndexBuilder::Lock( int nMaxIndexCount, int nIndexOffset, bool bAppend ) { Assert( m_pIndexBuffer ); m_bModify = false; m_nIndexOffset = nIndexOffset; m_nMaxIndexCount = nMaxIndexCount; bool bFirstLock = ( m_nBufferOffset == INVALID_BUFFER_OFFSET ); if ( bFirstLock ) { bAppend = false; } if ( !bAppend ) { m_nTotalIndexCount = 0; } Reset(); // Lock the index buffer if ( !m_pIndexBuffer->Lock( m_nMaxIndexCount, bAppend, *this ) ) { m_nMaxIndexCount = 0; return false; } if ( bFirstLock ) { m_nBufferOffset = m_nOffset; m_nBufferFirstIndex = m_nFirstIndex; } return true; } inline void CIndexBuilder::Unlock() { Assert( !m_bModify && m_pIndexBuffer ); m_pIndexBuffer->Unlock( m_nIndexCount, *this ); m_nTotalIndexCount += m_nIndexCount; m_nMaxIndexCount = 0; #ifdef _DEBUG // Null out our data... memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); #endif } inline void CIndexBuilder::SpewData() { m_pIndexBuffer->Spew( m_nIndexCount, *this ); } //----------------------------------------------------------------------------- // Binds this index buffer //----------------------------------------------------------------------------- inline void CIndexBuilder::Bind( IMatRenderContext *pContext ) { if ( m_pIndexBuffer && ( m_nBufferOffset != INVALID_BUFFER_OFFSET ) ) { pContext->BindIndexBuffer( m_pIndexBuffer, m_nBufferOffset ); } else { pContext->BindIndexBuffer( NULL, 0 ); } } //----------------------------------------------------------------------------- // Returns the byte offset //----------------------------------------------------------------------------- inline int CIndexBuilder::Offset() const { return m_nBufferOffset; } inline int CIndexBuilder::GetFirstIndex() const { return m_nBufferFirstIndex; } //----------------------------------------------------------------------------- // Begins, ends modification of the index buffer //----------------------------------------------------------------------------- inline void CIndexBuilder::Begin( IIndexBuffer *pIndexBuffer, int nMaxIndexCount, int nIndexOffset ) { Assert( pIndexBuffer && (!m_pIndexBuffer) ); m_pIndexBuffer = pIndexBuffer; m_nIndexCount = 0; m_nMaxIndexCount = nMaxIndexCount; m_nIndexOffset = nIndexOffset; m_bModify = false; // Lock the index buffer m_pIndexBuffer->Lock( m_nMaxIndexCount, false, *this ); // Point to the start of the buffers.. Reset(); } inline void CIndexBuilder::End( bool bSpewData ) { // Make sure they called Begin() Assert( !m_bModify ); if ( bSpewData ) { m_pIndexBuffer->Spew( m_nIndexCount, *this ); } // Unlock our buffers m_pIndexBuffer->Unlock( m_nIndexCount, *this ); m_pIndexBuffer = 0; m_nMaxIndexCount = 0; #ifdef _DEBUG // Null out our data... memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); #endif } //----------------------------------------------------------------------------- // Begins, ends modification of an existing index buffer which has already been filled out //----------------------------------------------------------------------------- inline void CIndexBuilder::BeginModify( IIndexBuffer* pIndexBuffer, int nFirstIndex, int nIndexCount, int nIndexOffset ) { m_pIndexBuffer = pIndexBuffer; m_nIndexCount = nIndexCount; m_nMaxIndexCount = nIndexCount; m_nIndexOffset = nIndexOffset; m_bModify = true; // Lock the vertex and index buffer m_pIndexBuffer->ModifyBegin( false, nFirstIndex, nIndexCount, *this ); // Point to the start of the buffers.. Reset(); } inline void CIndexBuilder::EndModify( bool bSpewData ) { Assert( m_pIndexBuffer ); Assert( m_bModify ); // Make sure they called BeginModify. if ( bSpewData ) { m_pIndexBuffer->Spew( m_nIndexCount, *this ); } // Unlock our buffers m_pIndexBuffer->ModifyEnd( *this ); m_pIndexBuffer = 0; m_nMaxIndexCount = 0; #ifdef _DEBUG // Null out our data... memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); #endif } //----------------------------------------------------------------------------- // FIXME: Remove! Backward compat so we can use this from a CMeshBuilder. //----------------------------------------------------------------------------- inline void CIndexBuilder::AttachBegin( IMesh* pMesh, int nMaxIndexCount, const MeshDesc_t &desc ) { m_pIndexBuffer = pMesh; m_nIndexCount = 0; m_nMaxIndexCount = nMaxIndexCount; m_bModify = false; // Copy relevant data from the mesh desc m_nIndexOffset = desc.m_nFirstVertex; m_pIndices = desc.m_pIndices; m_nIndexSize = desc.m_nIndexSize; // Point to the start of the buffers.. Reset(); } inline void CIndexBuilder::AttachEnd() { Assert( m_pIndexBuffer ); Assert( !m_bModify ); // Make sure they called AttachBegin. m_pIndexBuffer = 0; m_nMaxIndexCount = 0; #ifdef _DEBUG // Null out our data... memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); #endif } inline void CIndexBuilder::AttachBeginModify( IMesh* pMesh, int nFirstIndex, int nIndexCount, const MeshDesc_t &desc ) { m_pIndexBuffer = pMesh; m_nIndexCount = nIndexCount; m_nMaxIndexCount = nIndexCount; m_bModify = true; // Copy relevant data from the mesh desc m_nIndexOffset = desc.m_nFirstVertex; m_pIndices = desc.m_pIndices; m_nIndexSize = desc.m_nIndexSize; // Point to the start of the buffers.. Reset(); } inline void CIndexBuilder::AttachEndModify() { Assert( m_pIndexBuffer ); Assert( m_bModify ); // Make sure they called AttachBeginModify. m_pIndexBuffer = 0; m_nMaxIndexCount = 0; #ifdef _DEBUG // Null out our data... memset( (IndexDesc_t*)this, 0, sizeof(IndexDesc_t) ); #endif } //----------------------------------------------------------------------------- // Resets the index buffer builder so it points to the start of everything again //----------------------------------------------------------------------------- inline void CIndexBuilder::Reset() { m_nCurrentIndex = 0; } //----------------------------------------------------------------------------- // returns the number of indices //----------------------------------------------------------------------------- inline int CIndexBuilder::IndexCount() const { return m_nIndexCount; } //----------------------------------------------------------------------------- // Returns the total number of indices across all Locks() //----------------------------------------------------------------------------- inline int CIndexBuilder::TotalIndexCount() const { return m_nTotalIndexCount; } //----------------------------------------------------------------------------- // Advances the current index //----------------------------------------------------------------------------- inline void CIndexBuilder::AdvanceIndex() { m_nCurrentIndex += m_nIndexSize; if ( m_nCurrentIndex > m_nIndexCount ) { m_nIndexCount = m_nCurrentIndex; } } inline void CIndexBuilder::AdvanceIndices( int nIndices ) { m_nCurrentIndex += nIndices * m_nIndexSize; if ( m_nCurrentIndex > m_nIndexCount ) { m_nIndexCount = m_nCurrentIndex; } } //----------------------------------------------------------------------------- // Returns the current index //----------------------------------------------------------------------------- inline int CIndexBuilder::GetCurrentIndex() { return m_nCurrentIndex; } inline unsigned short const* CIndexBuilder::Index() const { Assert( m_nCurrentIndex < m_nMaxIndexCount ); return &m_pIndices[m_nCurrentIndex]; } inline void CIndexBuilder::SelectIndex( int nIndex ) { Assert( ( nIndex >= 0 ) && ( nIndex < m_nIndexCount ) ); m_nCurrentIndex = nIndex * m_nIndexSize; } //----------------------------------------------------------------------------- // Used to write data into the index buffer //----------------------------------------------------------------------------- inline void CIndexBuilder::Index( unsigned short nIndex ) { Assert( m_pIndices ); Assert( m_nCurrentIndex < m_nMaxIndexCount ); m_pIndices[ m_nCurrentIndex ] = (unsigned short)( m_nIndexOffset + nIndex ); } // Fast Index! No need to call advance index inline void CIndexBuilder::FastIndex( unsigned short nIndex ) { Assert( m_pIndices ); Assert( m_nCurrentIndex < m_nMaxIndexCount ); m_pIndices[m_nCurrentIndex] = (unsigned short)( m_nIndexOffset + nIndex ); m_nCurrentIndex += m_nIndexSize; m_nIndexCount = m_nCurrentIndex; } inline void CIndexBuilder::FastTriangle( int startVert ) { startVert += m_nIndexOffset; m_pIndices[m_nCurrentIndex+0] = startVert; m_pIndices[m_nCurrentIndex+1] = startVert + 1; m_pIndices[m_nCurrentIndex+2] = startVert + 2; AdvanceIndices(3); } inline void CIndexBuilder::FastQuad( int startVert ) { startVert += m_nIndexOffset; m_pIndices[m_nCurrentIndex+0] = startVert; m_pIndices[m_nCurrentIndex+1] = startVert + 1; m_pIndices[m_nCurrentIndex+2] = startVert + 2; m_pIndices[m_nCurrentIndex+3] = startVert; m_pIndices[m_nCurrentIndex+4] = startVert + 2; m_pIndices[m_nCurrentIndex+5] = startVert + 3; AdvanceIndices(6); } inline void CIndexBuilder::FastPolygon( int startVert, int triangleCount ) { unsigned short *pIndex = &m_pIndices[m_nCurrentIndex]; startVert += m_nIndexOffset; if ( !IsX360() ) { // NOTE: IndexSize is 1 or 0 (0 for alt-tab) // This prevents us from writing into bogus memory Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); triangleCount *= m_nIndexSize; } for ( int v = 0; v < triangleCount; ++v ) { *pIndex++ = startVert; *pIndex++ = startVert + v + 1; *pIndex++ = startVert + v + 2; } AdvanceIndices(triangleCount*3); } inline void CIndexBuilder::FastPolygonList( int startVert, int *pVertexCount, int polygonCount ) { unsigned short *pIndex = &m_pIndices[m_nCurrentIndex]; startVert += m_nIndexOffset; int indexOut = 0; if ( !IsX360() ) { // NOTE: IndexSize is 1 or 0 (0 for alt-tab) // This prevents us from writing into bogus memory Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); polygonCount *= m_nIndexSize; } for ( int i = 0; i < polygonCount; i++ ) { int vertexCount = pVertexCount[i]; int triangleCount = vertexCount-2; for ( int v = 0; v < triangleCount; ++v ) { *pIndex++ = startVert; *pIndex++ = startVert + v + 1; *pIndex++ = startVert + v + 2; } startVert += vertexCount; indexOut += triangleCount * 3; } AdvanceIndices(indexOut); } inline void CIndexBuilder::FastIndexList( const unsigned short *pIndexList, int startVert, int indexCount ) { unsigned short *pIndexOut = &m_pIndices[m_nCurrentIndex]; startVert += m_nIndexOffset; if ( !IsX360() ) { // NOTE: IndexSize is 1 or 0 (0 for alt-tab) // This prevents us from writing into bogus memory Assert( m_nIndexSize == 0 || m_nIndexSize == 1 ); indexCount *= m_nIndexSize; } for ( int i = 0; i < indexCount; ++i ) { pIndexOut[i] = startVert + pIndexList[i]; } AdvanceIndices(indexCount); } //----------------------------------------------------------------------------- // NOTE: This version is the one you really want to achieve write-combining; // Write combining only works if you write in 4 bytes chunks. //----------------------------------------------------------------------------- inline void CIndexBuilder::FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ) { Assert( m_pIndices ); Assert( m_nCurrentIndex < m_nMaxIndexCount - 1 ); // Assert( ( (int)( &m_pIndices[m_nCurrentIndex] ) & 0x3 ) == 0 ); #ifndef _X360 unsigned int nIndices = ( (unsigned int)nIndex1 + m_nIndexOffset ) | ( ( (unsigned int)nIndex2 + m_nIndexOffset ) << 16 ); #else unsigned int nIndices = ( (unsigned int)nIndex2 + m_nIndexOffset ) | ( ( (unsigned int)nIndex1 + m_nIndexOffset ) << 16 ); #endif *(int*)( &m_pIndices[m_nCurrentIndex] ) = nIndices; m_nCurrentIndex += m_nIndexSize + m_nIndexSize; m_nIndexCount = m_nCurrentIndex; } //----------------------------------------------------------------------------- // Generates indices for a particular primitive type //----------------------------------------------------------------------------- inline void CIndexBuilder::GenerateIndices( MaterialPrimitiveType_t primitiveType, int nIndexCount ) { // FIXME: How to make this work with short vs int sized indices? // Don't generate indices if we've got an empty buffer if ( m_nIndexSize == 0 ) return; int nMaxIndices = m_nMaxIndexCount - m_nCurrentIndex; nIndexCount = Min( nMaxIndices, nIndexCount ); if ( nIndexCount == 0 ) return; unsigned short *pIndices = &m_pIndices[m_nCurrentIndex]; switch( primitiveType ) { case MATERIAL_INSTANCED_QUADS: Assert(0); // Shouldn't get here (this primtype is unindexed) break; case MATERIAL_QUADS: GenerateQuadIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); break; case MATERIAL_POLYGON: GeneratePolygonIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); break; case MATERIAL_LINE_STRIP: GenerateLineStripIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); break; case MATERIAL_LINE_LOOP: GenerateLineLoopIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); break; case MATERIAL_POINTS: Assert(0); // Shouldn't get here (this primtype is unindexed) break; default: GenerateSequentialIndexBuffer( pIndices, nIndexCount, m_nIndexOffset ); break; } AdvanceIndices( nIndexCount ); } //----------------------------------------------------------------------------- // // Helper class used to define meshes // //----------------------------------------------------------------------------- //class CMeshBuilder : private MeshDesc_t // hack fixme class CMeshBuilder : public MeshDesc_t { public: CMeshBuilder(); ~CMeshBuilder() { Assert(!m_pMesh); } // if this fires you did a Begin() without an End() operator CIndexBuilder&() { return m_IndexBuilder; } // This must be called before Begin, if a vertex buffer with a compressed format is to be used void SetCompressionType( VertexCompressionType_t compressionType ); // Locks the vertex buffer // (*cannot* use the Index() call below) void Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int numPrimitives ); // Locks the vertex buffer, can specify arbitrary index lists // (must use the Index() call below) void Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ); void Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ); // forward compat void Begin( IVertexBuffer *pVertexBuffer, MaterialPrimitiveType_t type, int numPrimitives ); void Begin( IVertexBuffer *pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ); void Begin( IVertexBuffer *pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ); // Use this when you're done writing // Set bDraw to true to call m_pMesh->Draw automatically. void End( bool bSpewData = false, bool bDraw = false ); // Locks the vertex buffer to modify existing data // Passing nVertexCount == -1 says to lock all the vertices for modification. // Pass 0 for nIndexCount to not lock the index buffer. void BeginModify( IMesh *pMesh, int nFirstVertex = 0, int nVertexCount = -1, int nFirstIndex = 0, int nIndexCount = 0 ); void EndModify( bool bSpewData = false ); // A helper method since this seems to be done a whole bunch. void DrawQuad( IMesh* pMesh, const float *v1, const float *v2, const float *v3, const float *v4, unsigned char const *pColor, bool wireframe = false ); // returns the number of indices and vertices int VertexCount() const; int IndexCount() const; // Resets the mesh builder so it points to the start of everything again void Reset(); // Returns the size of the vertex int VertexSize() { return m_ActualVertexSize; } // returns the data size of a given texture coordinate int TextureCoordinateSize( int nTexCoordNumber ) { return m_VertexSize_TexCoord[ nTexCoordNumber ]; } // Returns the base vertex memory pointer void* BaseVertexData(); // Selects the nth Vertex and Index void SelectVertex( int idx ); void SelectIndex( int idx ); // Given an index, point to the associated vertex void SelectVertexFromIndex( int idx ); // Advances the current vertex and index by one void AdvanceVertex(); template void AdvanceVertexF(); void AdvanceVertices( int nVerts ); void AdvanceIndex(); void AdvanceIndices( int nIndices ); int GetCurrentVertex(); int GetCurrentIndex(); // Data retrieval... const float *Position() const; const float *Normal() const; unsigned int Color() const; unsigned char *Specular() const; const float *TexCoord( int stage ) const; const float *TangentS() const; const float *TangentT() const; const float *BoneWeight() const; float Wrinkle() const; int NumBoneWeights() const; #ifndef NEW_SKINNING unsigned char *BoneMatrix() const; #else float *BoneMatrix() const; #endif unsigned short const *Index() const; // position setting void Position3f( float x, float y, float z ); void Position3fv( const float *v ); // normal setting void Normal3f( float nx, float ny, float nz ); void Normal3fv( const float *n ); void NormalDelta3fv( const float *n ); void NormalDelta3f( float nx, float ny, float nz ); // normal setting (templatized for code which needs to support compressed vertices) template void CompressedNormal3f( float nx, float ny, float nz ); template void CompressedNormal3fv( const float *n ); // color setting void Color3f( float r, float g, float b ); void Color3fv( const float *rgb ); void Color4f( float r, float g, float b, float a ); void Color4fv( const float *rgba ); // Faster versions of color void Color3ub( unsigned char r, unsigned char g, unsigned char b ); void Color3ubv( unsigned char const* rgb ); void Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); void Color4ubv( unsigned char const* rgba ); // specular color setting void Specular3f( float r, float g, float b ); void Specular3fv( const float *rgb ); void Specular4f( float r, float g, float b, float a ); void Specular4fv( const float *rgba ); // Faster version of specular void Specular3ub( unsigned char r, unsigned char g, unsigned char b ); void Specular3ubv( unsigned char const *c ); void Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ); void Specular4ubv( unsigned char const *c ); // texture coordinate setting void TexCoord1f( int stage, float s ); void TexCoord2f( int stage, float s, float t ); void TexCoord2fv( int stage, const float *st ); void TexCoord3f( int stage, float s, float t, float u ); void TexCoord3fv( int stage, const float *stu ); void TexCoord4f( int stage, float s, float t, float u, float w ); void TexCoord4fv( int stage, const float *stuv ); void TexCoordSubRect2f( int stage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ); void TexCoordSubRect2fv( int stage, const float *st, const float *offset, const float *scale ); // tangent space void TangentS3f( float sx, float sy, float sz ); void TangentS3fv( const float *s ); void TangentT3f( float tx, float ty, float tz ); void TangentT3fv( const float *t ); // Wrinkle void Wrinkle1f( float flWrinkle ); // bone weights void BoneWeight( int idx, float weight ); // bone weights (templatized for code which needs to support compressed vertices) template void CompressedBoneWeight3fv( const float * pWeights ); // bone matrix index void BoneMatrix( int idx, int matrixIndex ); // Generic per-vertex data void UserData( const float *pData ); // Generic per-vertex data (templatized for code which needs to support compressed vertices) template void CompressedUserData( const float* pData ); // Used to define the indices (only used if you aren't using primitives) void Index( unsigned short index ); // NOTE: Use this one to get write combining! Much faster than the other version of FastIndex // Fast Index! No need to call advance index, and no random access allowed void FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ); // Fast Index! No need to call advance index, and no random access allowed void FastIndex( unsigned short index ); // Fast Vertex! No need to call advance vertex, and no random access allowed. // WARNING - these are low level functions that are intended only for use // in the software vertex skinner. void FastVertex( const ModelVertexDX7_t &vertex ); void FastVertexSSE( const ModelVertexDX7_t &vertex ); // store 4 dx7 vertices fast. for special sse dx7 pipeline void Fast4VerticesSSE( ModelVertexDX7_t const *vtx_a, ModelVertexDX7_t const *vtx_b, ModelVertexDX7_t const *vtx_c, ModelVertexDX7_t const *vtx_d); void FastVertex( const ModelVertexDX8_t &vertex ); void FastVertexSSE( const ModelVertexDX8_t &vertex ); // Add number of verts and current vert since FastVertexxx routines do not update. void FastAdvanceNVertices(int n); #if defined( _X360 ) void VertexDX8ToX360( const ModelVertexDX8_t &vertex ); #endif private: // Computes number of verts and indices void ComputeNumVertsAndIndices( int *pMaxVertices, int *pMaxIndices, MaterialPrimitiveType_t type, int nPrimitiveCount ); int IndicesFromVertices( MaterialPrimitiveType_t type, int nVertexCount ); // The mesh we're modifying IMesh *m_pMesh; MaterialPrimitiveType_t m_Type; // Generate indices? bool m_bGenerateIndices; CIndexBuilder m_IndexBuilder; CVertexBuilder m_VertexBuilder; }; //----------------------------------------------------------------------------- // Forward compat //----------------------------------------------------------------------------- inline void CMeshBuilder::Begin( IVertexBuffer* pVertexBuffer, MaterialPrimitiveType_t type, int numPrimitives ) { Assert( 0 ); // Begin( pVertexBuffer->GetMesh(), type, numPrimitives ); } inline void CMeshBuilder::Begin( IVertexBuffer* pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ) { Assert( 0 ); // Begin( pVertexBuffer->GetMesh(), type, nVertexCount, nIndexCount, nFirstVertex ); } inline void CMeshBuilder::Begin( IVertexBuffer* pVertexBuffer, IIndexBuffer *pIndexBuffer, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ) { Assert( 0 ); // Begin( pVertexBuffer->GetMesh(), type, nVertexCount, nIndexCount ); } //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- inline CMeshBuilder::CMeshBuilder() : m_pMesh(0), m_bGenerateIndices(false) { } //----------------------------------------------------------------------------- // Computes the number of verts and indices based on primitive type and count //----------------------------------------------------------------------------- inline void CMeshBuilder::ComputeNumVertsAndIndices( int *pMaxVertices, int *pMaxIndices, MaterialPrimitiveType_t type, int nPrimitiveCount ) { switch(type) { case MATERIAL_POINTS: *pMaxVertices = *pMaxIndices = nPrimitiveCount; break; case MATERIAL_LINES: *pMaxVertices = *pMaxIndices = nPrimitiveCount * 2; break; case MATERIAL_LINE_STRIP: *pMaxVertices = nPrimitiveCount + 1; *pMaxIndices = nPrimitiveCount * 2; break; case MATERIAL_LINE_LOOP: *pMaxVertices = nPrimitiveCount; *pMaxIndices = nPrimitiveCount * 2; break; case MATERIAL_TRIANGLES: *pMaxVertices = *pMaxIndices = nPrimitiveCount * 3; break; case MATERIAL_TRIANGLE_STRIP: *pMaxVertices = *pMaxIndices = nPrimitiveCount + 2; break; case MATERIAL_QUADS: *pMaxVertices = nPrimitiveCount * 4; *pMaxIndices = nPrimitiveCount * 6; break; case MATERIAL_INSTANCED_QUADS: *pMaxVertices = nPrimitiveCount; *pMaxIndices = 0; // This primtype is unindexed break; case MATERIAL_POLYGON: *pMaxVertices = nPrimitiveCount; *pMaxIndices = (nPrimitiveCount - 2) * 3; break; default: *pMaxVertices = 0; *pMaxIndices = 0; Assert(0); } // FIXME: need to get this from meshdx8.cpp, or move it to somewhere common Assert( *pMaxVertices <= 32768 ); Assert( *pMaxIndices <= 32768 ); } inline int CMeshBuilder::IndicesFromVertices( MaterialPrimitiveType_t type, int nVertexCount ) { switch( type ) { case MATERIAL_QUADS: Assert( (nVertexCount & 0x3) == 0 ); return (nVertexCount * 6) / 4; case MATERIAL_INSTANCED_QUADS: // This primtype is unindexed return 0; case MATERIAL_POLYGON: Assert( nVertexCount >= 3 ); return (nVertexCount - 2) * 3; case MATERIAL_LINE_STRIP: Assert( nVertexCount >= 2 ); return (nVertexCount - 1) * 2; case MATERIAL_LINE_LOOP: Assert( nVertexCount >= 3 ); return nVertexCount * 2; default: return nVertexCount; } } //----------------------------------------------------------------------------- // Specify the type of vertex compression that this CMeshBuilder will perform //----------------------------------------------------------------------------- inline void CMeshBuilder::SetCompressionType( VertexCompressionType_t vertexCompressionType ) { m_VertexBuilder.SetCompressionType( vertexCompressionType ); } //----------------------------------------------------------------------------- // Begins modifying the mesh //----------------------------------------------------------------------------- inline void CMeshBuilder::Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int numPrimitives ) { Assert( pMesh && (!m_pMesh) ); Assert( type != MATERIAL_HETEROGENOUS ); m_pMesh = pMesh; m_bGenerateIndices = true; m_Type = type; int nMaxVertexCount, nMaxIndexCount; ComputeNumVertsAndIndices( &nMaxVertexCount, &nMaxIndexCount, type, numPrimitives ); switch( type ) { case MATERIAL_INSTANCED_QUADS: m_pMesh->SetPrimitiveType( MATERIAL_INSTANCED_QUADS ); break; case MATERIAL_QUADS: case MATERIAL_POLYGON: m_pMesh->SetPrimitiveType( MATERIAL_TRIANGLES ); break; case MATERIAL_LINE_STRIP: case MATERIAL_LINE_LOOP: m_pMesh->SetPrimitiveType( MATERIAL_LINES ); break; default: m_pMesh->SetPrimitiveType( type ); } // Lock the mesh m_pMesh->LockMesh( nMaxVertexCount, nMaxIndexCount, *this ); m_IndexBuilder.AttachBegin( pMesh, nMaxIndexCount, *this ); m_VertexBuilder.AttachBegin( pMesh, nMaxVertexCount, *this ); // Point to the start of the index and vertex buffers Reset(); } inline void CMeshBuilder::Begin( IMesh *pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount, int *nFirstVertex ) { Begin( pMesh, type, nVertexCount, nIndexCount ); *nFirstVertex = m_VertexBuilder.m_nFirstVertex * m_VertexBuilder.VertexSize(); } inline void CMeshBuilder::Begin( IMesh* pMesh, MaterialPrimitiveType_t type, int nVertexCount, int nIndexCount ) { Assert( pMesh && (!m_pMesh) ); // NOTE: We can't specify the indices when we use quads, polygons, or // linestrips; they aren't actually directly supported by // the material system Assert( (type != MATERIAL_QUADS) && (type != MATERIAL_INSTANCED_QUADS) && (type != MATERIAL_POLYGON) && (type != MATERIAL_LINE_STRIP) && (type != MATERIAL_LINE_LOOP)); // Dx8 doesn't support indexed points... Assert( type != MATERIAL_POINTS ); m_pMesh = pMesh; m_bGenerateIndices = false; m_Type = type; // Set the primitive type m_pMesh->SetPrimitiveType( type ); // Lock the vertex and index buffer m_pMesh->LockMesh( nVertexCount, nIndexCount, *this ); m_IndexBuilder.AttachBegin( pMesh, nIndexCount, *this ); m_VertexBuilder.AttachBegin( pMesh, nVertexCount, *this ); // Point to the start of the buffers.. Reset(); } //----------------------------------------------------------------------------- // Use this when you're done modifying the mesh //----------------------------------------------------------------------------- inline void CMeshBuilder::End( bool bSpewData, bool bDraw ) { if ( m_bGenerateIndices ) { int nIndexCount = IndicesFromVertices( m_Type, m_VertexBuilder.VertexCount() ); m_IndexBuilder.GenerateIndices( m_Type, nIndexCount ); } if ( bSpewData ) { m_pMesh->Spew( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); } #ifdef _DEBUG m_pMesh->ValidateData( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); #endif // Unlock our buffers m_pMesh->UnlockMesh( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); m_IndexBuilder.AttachEnd(); m_VertexBuilder.AttachEnd(); if ( bDraw ) { m_pMesh->Draw(); } m_pMesh = 0; #ifdef _DEBUG memset( (MeshDesc_t*)this, 0, sizeof(MeshDesc_t) ); #endif } //----------------------------------------------------------------------------- // Locks the vertex buffer to modify existing data //----------------------------------------------------------------------------- inline void CMeshBuilder::BeginModify( IMesh* pMesh, int nFirstVertex, int nVertexCount, int nFirstIndex, int nIndexCount ) { Assert( pMesh && (!m_pMesh) ); if (nVertexCount < 0) { nVertexCount = pMesh->VertexCount(); } m_pMesh = pMesh; m_bGenerateIndices = false; // Locks mesh for modifying pMesh->ModifyBeginEx( false, nFirstVertex, nVertexCount, nFirstIndex, nIndexCount, *this ); m_IndexBuilder.AttachBeginModify( pMesh, nFirstIndex, nIndexCount, *this ); m_VertexBuilder.AttachBeginModify( pMesh, nFirstVertex, nVertexCount, *this ); // Point to the start of the buffers.. Reset(); } inline void CMeshBuilder::EndModify( bool bSpewData ) { Assert( m_pMesh ); if (bSpewData) { m_pMesh->Spew( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); } #ifdef _DEBUG m_pMesh->ValidateData( m_VertexBuilder.VertexCount(), m_IndexBuilder.IndexCount(), *this ); #endif // Unlocks mesh m_pMesh->ModifyEnd( *this ); m_pMesh = 0; m_IndexBuilder.AttachEndModify(); m_VertexBuilder.AttachEndModify(); #ifdef _DEBUG // Null out our pointers... memset( (MeshDesc_t*)this, 0, sizeof(MeshDesc_t) ); #endif } //----------------------------------------------------------------------------- // Resets the mesh builder so it points to the start of everything again //----------------------------------------------------------------------------- inline void CMeshBuilder::Reset() { m_IndexBuilder.Reset(); m_VertexBuilder.Reset(); } //----------------------------------------------------------------------------- // Selects the current Vertex and Index //----------------------------------------------------------------------------- FORCEINLINE void CMeshBuilder::SelectVertex( int nIndex ) { m_VertexBuilder.SelectVertex( nIndex ); } inline void CMeshBuilder::SelectVertexFromIndex( int idx ) { // NOTE: This index is expected to be relative int vertIdx = idx - m_nFirstVertex; SelectVertex( vertIdx ); } FORCEINLINE void CMeshBuilder::SelectIndex( int idx ) { m_IndexBuilder.SelectIndex( idx ); } //----------------------------------------------------------------------------- // Advances the current vertex and index by one //----------------------------------------------------------------------------- template FORCEINLINE void CMeshBuilder::AdvanceVertexF() { m_VertexBuilder.AdvanceVertexF(); } FORCEINLINE void CMeshBuilder::AdvanceVertex() { m_VertexBuilder.AdvanceVertex(); } FORCEINLINE void CMeshBuilder::AdvanceVertices( int nVertexCount ) { m_VertexBuilder.AdvanceVertices( nVertexCount ); } FORCEINLINE void CMeshBuilder::AdvanceIndex() { m_IndexBuilder.AdvanceIndex(); } FORCEINLINE void CMeshBuilder::AdvanceIndices( int nIndices ) { m_IndexBuilder.AdvanceIndices( nIndices ); } FORCEINLINE int CMeshBuilder::GetCurrentVertex() { return m_VertexBuilder.GetCurrentVertex(); } FORCEINLINE int CMeshBuilder::GetCurrentIndex() { return m_IndexBuilder.GetCurrentIndex(); } //----------------------------------------------------------------------------- // A helper method since this seems to be done a whole bunch. //----------------------------------------------------------------------------- inline void CMeshBuilder::DrawQuad( IMesh* pMesh, const float* v1, const float* v2, const float* v3, const float* v4, unsigned char const* pColor, bool wireframe ) { if (!wireframe) { Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 ); Position3fv (v1); Color4ubv( pColor ); AdvanceVertexF(); Position3fv (v2); Color4ubv( pColor ); AdvanceVertexF(); Position3fv (v4); Color4ubv( pColor ); AdvanceVertexF(); Position3fv (v3); Color4ubv( pColor ); AdvanceVertexF(); } else { Begin( pMesh, MATERIAL_LINE_LOOP, 4 ); Position3fv (v1); Color4ubv( pColor ); AdvanceVertexF(); Position3fv (v2); Color4ubv( pColor ); AdvanceVertexF(); Position3fv (v3); Color4ubv( pColor ); AdvanceVertexF(); Position3fv (v4); Color4ubv( pColor ); AdvanceVertexF(); } End(); pMesh->Draw(); } //----------------------------------------------------------------------------- // returns the number of indices and vertices //----------------------------------------------------------------------------- FORCEINLINE int CMeshBuilder::VertexCount() const { return m_VertexBuilder.VertexCount(); } FORCEINLINE int CMeshBuilder::IndexCount() const { return m_IndexBuilder.IndexCount(); } //----------------------------------------------------------------------------- // Returns the base vertex memory pointer //----------------------------------------------------------------------------- FORCEINLINE void* CMeshBuilder::BaseVertexData() { return m_VertexBuilder.BaseVertexData(); } //----------------------------------------------------------------------------- // Data retrieval... //----------------------------------------------------------------------------- FORCEINLINE const float* CMeshBuilder::Position() const { return m_VertexBuilder.Position(); } FORCEINLINE const float* CMeshBuilder::Normal() const { return m_VertexBuilder.Normal(); } FORCEINLINE unsigned int CMeshBuilder::Color() const { return m_VertexBuilder.Color(); } FORCEINLINE unsigned char *CMeshBuilder::Specular() const { return m_VertexBuilder.Specular(); } FORCEINLINE const float* CMeshBuilder::TexCoord( int nStage ) const { return m_VertexBuilder.TexCoord( nStage ); } FORCEINLINE const float* CMeshBuilder::TangentS() const { return m_VertexBuilder.TangentS(); } FORCEINLINE const float* CMeshBuilder::TangentT() const { return m_VertexBuilder.TangentT(); } FORCEINLINE float CMeshBuilder::Wrinkle() const { return m_VertexBuilder.Wrinkle(); } FORCEINLINE const float* CMeshBuilder::BoneWeight() const { return m_VertexBuilder.BoneWeight(); } FORCEINLINE int CMeshBuilder::NumBoneWeights() const { return m_VertexBuilder.NumBoneWeights(); } FORCEINLINE unsigned short const* CMeshBuilder::Index() const { return m_IndexBuilder.Index(); } //----------------------------------------------------------------------------- // Index //----------------------------------------------------------------------------- FORCEINLINE void CMeshBuilder::Index( unsigned short idx ) { m_IndexBuilder.Index( idx ); } //----------------------------------------------------------------------------- // Fast Index! No need to call advance index //----------------------------------------------------------------------------- FORCEINLINE void CMeshBuilder::FastIndex( unsigned short idx ) { m_IndexBuilder.FastIndex( idx ); } // NOTE: Use this one to get write combining! Much faster than the other version of FastIndex // Fast Index! No need to call advance index, and no random access allowed FORCEINLINE void CMeshBuilder::FastIndex2( unsigned short nIndex1, unsigned short nIndex2 ) { m_IndexBuilder.FastIndex2( nIndex1, nIndex2 ); } //----------------------------------------------------------------------------- // For use with the FastVertex methods, advances the current vertex by N //----------------------------------------------------------------------------- FORCEINLINE void CMeshBuilder::FastAdvanceNVertices( int nVertexCount ) { m_VertexBuilder.FastAdvanceNVertices( nVertexCount ); } //----------------------------------------------------------------------------- // Fast Vertex! No need to call advance vertex, and no random access allowed //----------------------------------------------------------------------------- FORCEINLINE void CMeshBuilder::FastVertex( const ModelVertexDX7_t &vertex ) { m_VertexBuilder.FastVertex( vertex ); } FORCEINLINE void CMeshBuilder::FastVertexSSE( const ModelVertexDX7_t &vertex ) { m_VertexBuilder.FastVertexSSE( vertex ); } FORCEINLINE void CMeshBuilder::Fast4VerticesSSE( const ModelVertexDX7_t *vtx_a, const ModelVertexDX7_t *vtx_b, const ModelVertexDX7_t *vtx_c, const ModelVertexDX7_t *vtx_d ) { m_VertexBuilder.Fast4VerticesSSE( vtx_a, vtx_b, vtx_c, vtx_d ); } FORCEINLINE void CMeshBuilder::FastVertex( const ModelVertexDX8_t &vertex ) { m_VertexBuilder.FastVertex( vertex ); } FORCEINLINE void CMeshBuilder::FastVertexSSE( const ModelVertexDX8_t &vertex ) { m_VertexBuilder.FastVertexSSE( vertex ); } //----------------------------------------------------------------------------- // Copies a vertex into the x360 format //----------------------------------------------------------------------------- #if defined( _X360 ) inline void CMeshBuilder::VertexDX8ToX360( const ModelVertexDX8_t &vertex ) { m_VertexBuilder.VertexDX8ToX360( vertex ); } #endif //----------------------------------------------------------------------------- // Vertex field setting methods //----------------------------------------------------------------------------- FORCEINLINE void CMeshBuilder::Position3f( float x, float y, float z ) { m_VertexBuilder.Position3f( x, y, z ); } FORCEINLINE void CMeshBuilder::Position3fv( const float *v ) { m_VertexBuilder.Position3fv( v ); } FORCEINLINE void CMeshBuilder::Normal3f( float nx, float ny, float nz ) { m_VertexBuilder.Normal3f( nx, ny, nz ); } FORCEINLINE void CMeshBuilder::Normal3fv( const float *n ) { m_VertexBuilder.Normal3fv( n ); } FORCEINLINE void CMeshBuilder::NormalDelta3f( float nx, float ny, float nz ) { m_VertexBuilder.NormalDelta3f( nx, ny, nz ); } FORCEINLINE void CMeshBuilder::NormalDelta3fv( const float *n ) { m_VertexBuilder.NormalDelta3fv( n ); } FORCEINLINE void CMeshBuilder::Color3f( float r, float g, float b ) { m_VertexBuilder.Color3f( r, g, b ); } FORCEINLINE void CMeshBuilder::Color3fv( const float *rgb ) { m_VertexBuilder.Color3fv( rgb ); } FORCEINLINE void CMeshBuilder::Color4f( float r, float g, float b, float a ) { m_VertexBuilder.Color4f( r, g ,b, a ); } FORCEINLINE void CMeshBuilder::Color4fv( const float *rgba ) { m_VertexBuilder.Color4fv( rgba ); } FORCEINLINE void CMeshBuilder::Color3ub( unsigned char r, unsigned char g, unsigned char b ) { m_VertexBuilder.Color3ub( r, g, b ); } FORCEINLINE void CMeshBuilder::Color3ubv( unsigned char const* rgb ) { m_VertexBuilder.Color3ubv( rgb ); } FORCEINLINE void CMeshBuilder::Color4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) { m_VertexBuilder.Color4ub( r, g, b, a ); } FORCEINLINE void CMeshBuilder::Color4ubv( unsigned char const* rgba ) { m_VertexBuilder.Color4ubv( rgba ); } FORCEINLINE void CMeshBuilder::Specular3f( float r, float g, float b ) { m_VertexBuilder.Specular3f( r, g, b ); } FORCEINLINE void CMeshBuilder::Specular3fv( const float *rgb ) { m_VertexBuilder.Specular3fv( rgb ); } FORCEINLINE void CMeshBuilder::Specular4f( float r, float g, float b, float a ) { m_VertexBuilder.Specular4f( r, g, b, a ); } FORCEINLINE void CMeshBuilder::Specular4fv( const float *rgba ) { m_VertexBuilder.Specular4fv( rgba ); } FORCEINLINE void CMeshBuilder::Specular3ub( unsigned char r, unsigned char g, unsigned char b ) { m_VertexBuilder.Specular3ub( r, g, b ); } FORCEINLINE void CMeshBuilder::Specular3ubv( unsigned char const *c ) { m_VertexBuilder.Specular3ubv( c ); } FORCEINLINE void CMeshBuilder::Specular4ub( unsigned char r, unsigned char g, unsigned char b, unsigned char a ) { m_VertexBuilder.Specular4ub( r, g, b, a ); } FORCEINLINE void CMeshBuilder::Specular4ubv( unsigned char const *c ) { m_VertexBuilder.Specular4ubv( c ); } FORCEINLINE void CMeshBuilder::TexCoord1f( int nStage, float s ) { m_VertexBuilder.TexCoord1f( nStage, s ); } FORCEINLINE void CMeshBuilder::TexCoord2f( int nStage, float s, float t ) { m_VertexBuilder.TexCoord2f( nStage, s, t ); } FORCEINLINE void CMeshBuilder::TexCoord2fv( int nStage, const float *st ) { m_VertexBuilder.TexCoord2fv( nStage, st ); } FORCEINLINE void CMeshBuilder::TexCoord3f( int nStage, float s, float t, float u ) { m_VertexBuilder.TexCoord3f( nStage, s, t, u ); } FORCEINLINE void CMeshBuilder::TexCoord3fv( int nStage, const float *stu ) { m_VertexBuilder.TexCoord3fv( nStage, stu ); } FORCEINLINE void CMeshBuilder::TexCoord4f( int nStage, float s, float t, float u, float v ) { m_VertexBuilder.TexCoord4f( nStage, s, t, u, v ); } FORCEINLINE void CMeshBuilder::TexCoord4fv( int nStage, const float *stuv ) { m_VertexBuilder.TexCoord4fv( nStage, stuv ); } FORCEINLINE void CMeshBuilder::TexCoordSubRect2f( int nStage, float s, float t, float offsetS, float offsetT, float scaleS, float scaleT ) { m_VertexBuilder.TexCoordSubRect2f( nStage, s, t, offsetS, offsetT, scaleS, scaleT ); } FORCEINLINE void CMeshBuilder::TexCoordSubRect2fv( int nStage, const float *st, const float *offset, const float *scale ) { m_VertexBuilder.TexCoordSubRect2fv( nStage, st, offset, scale ); } FORCEINLINE void CMeshBuilder::TangentS3f( float sx, float sy, float sz ) { m_VertexBuilder.TangentS3f( sx, sy, sz ); } FORCEINLINE void CMeshBuilder::TangentS3fv( const float* s ) { m_VertexBuilder.TangentS3fv( s ); } FORCEINLINE void CMeshBuilder::TangentT3f( float tx, float ty, float tz ) { m_VertexBuilder.TangentT3f( tx, ty, tz ); } FORCEINLINE void CMeshBuilder::TangentT3fv( const float* t ) { m_VertexBuilder.TangentT3fv( t ); } FORCEINLINE void CMeshBuilder::Wrinkle1f( float flWrinkle ) { m_VertexBuilder.Wrinkle1f( flWrinkle ); } FORCEINLINE void CMeshBuilder::BoneWeight( int nIndex, float flWeight ) { m_VertexBuilder.BoneWeight( nIndex, flWeight ); } template FORCEINLINE void CMeshBuilder::CompressedBoneWeight3fv( const float * pWeights ) { m_VertexBuilder.CompressedBoneWeight3fv( pWeights ); } FORCEINLINE void CMeshBuilder::BoneMatrix( int nIndex, int nMatrixIdx ) { m_VertexBuilder.BoneMatrix( nIndex, nMatrixIdx ); } FORCEINLINE void CMeshBuilder::UserData( const float* pData ) { m_VertexBuilder.UserData( pData ); } template FORCEINLINE void CMeshBuilder::CompressedUserData( const float* pData ) { m_VertexBuilder.CompressedUserData( pData ); } //----------------------------------------------------------------------------- // Templatized vertex field setting methods which support compression //----------------------------------------------------------------------------- template FORCEINLINE void CMeshBuilder::CompressedNormal3f( float nx, float ny, float nz ) { m_VertexBuilder.CompressedNormal3f( nx, ny, nz ); } template FORCEINLINE void CMeshBuilder::CompressedNormal3fv( const float *n ) { m_VertexBuilder.CompressedNormal3fv( n ); } #endif // IMESH_H