diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /public/materialsystem/imesh.h | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'public/materialsystem/imesh.h')
| -rw-r--r-- | public/materialsystem/imesh.h | 4049 |
1 files changed, 4049 insertions, 0 deletions
diff --git a/public/materialsystem/imesh.h b/public/materialsystem/imesh.h new file mode 100644 index 0000000..3da443b --- /dev/null +++ b/public/materialsystem/imesh.h @@ -0,0 +1,4049 @@ +//========= 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 <float.h> +#include <string.h> +#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<float *>( + reinterpret_cast<unsigned char *>(pBufferPointer) + + nVertexCount * vertexSize); +} + +inline const float *OffsetFloatPointer( const float *pBufferPointer, int nVertexCount, int vertexSize ) +{ + return reinterpret_cast<const float*>( + reinterpret_cast<unsigned char const*>(pBufferPointer) + + nVertexCount * vertexSize); +} + +inline void IncrementFloatPointer( float* &pBufferPointer, int vertexSize ) +{ + pBufferPointer = reinterpret_cast<float*>( reinterpret_cast<unsigned char*>( 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<int nFlags, int nNumTexCoords> 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 <VertexCompressionType_t T> void CompressedNormal3f( float nx, float ny, float nz ); + template <VertexCompressionType_t T> 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 <VertexCompressionType_t T> 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 <VertexCompressionType_t T> 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<VertexDesc_t*>( 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<VertexDesc_t*>( this ), static_cast<const VertexDesc_t*>( &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<VertexDesc_t*>( 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<VertexDesc_t*>( this ), static_cast<const VertexDesc_t*>( &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<VertexDesc_t*>( 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<int nFlags, int nNumTexCoords> 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<VTX_HAVEALL, 8>(); +} + + +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 <VertexCompressionType_t T> 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 <VertexCompressionType_t T> inline void CVertexBuilder::CompressedNormal3fv( const float *n ) +{ + Assert( n ); + CompressedNormal3f<T>( 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 <VertexCompressionType_t T> 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 <VertexCompressionType_t T> 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<int nFlags, int nNumTexCoords> 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 <VertexCompressionType_t T> void CompressedNormal3f( float nx, float ny, float nz ); + template <VertexCompressionType_t T> 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 <VertexCompressionType_t T> 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 <VertexCompressionType_t T> 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<int nFlags, int nNumTexCoords> FORCEINLINE void CMeshBuilder::AdvanceVertexF() +{ + m_VertexBuilder.AdvanceVertexF<nFlags, nNumTexCoords>(); +} +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<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + + Position3fv (v2); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + + Position3fv (v4); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + + Position3fv (v3); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + } + else + { + Begin( pMesh, MATERIAL_LINE_LOOP, 4 ); + Position3fv (v1); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + + Position3fv (v2); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + + Position3fv (v3); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + + Position3fv (v4); + Color4ubv( pColor ); + AdvanceVertexF<VTX_HAVEPOS | VTX_HAVECOLOR, 0>(); + } + + 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 <VertexCompressionType_t T> FORCEINLINE void CMeshBuilder::CompressedBoneWeight3fv( const float * pWeights ) +{ + m_VertexBuilder.CompressedBoneWeight3fv<T>( 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 <VertexCompressionType_t T> FORCEINLINE void CMeshBuilder::CompressedUserData( const float* pData ) +{ + m_VertexBuilder.CompressedUserData<T>( pData ); +} + +//----------------------------------------------------------------------------- +// Templatized vertex field setting methods which support compression +//----------------------------------------------------------------------------- + +template <VertexCompressionType_t T> FORCEINLINE void CMeshBuilder::CompressedNormal3f( float nx, float ny, float nz ) +{ + m_VertexBuilder.CompressedNormal3f<T>( nx, ny, nz ); +} + +template <VertexCompressionType_t T> FORCEINLINE void CMeshBuilder::CompressedNormal3fv( const float *n ) +{ + m_VertexBuilder.CompressedNormal3fv<T>( n ); +} + +#endif // IMESH_H |