You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4096 lines
128 KiB
4096 lines
128 KiB
//========= 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() = default; |
|
CPrimList( int nFirstIndex, int nIndexCount ); |
|
|
|
int m_FirstIndex; |
|
int m_NumIndices; |
|
}; |
|
|
|
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 ) |
|
{ |
|
#if defined(__arm__) || defined(__aarch64__) || defined(PLATFORM_WINDOWS_PC64) |
|
FastVertexSSE( vertex ); |
|
#else |
|
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 |
|
#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 ) && !defined( PLATFORM_64BITS ) |
|
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) || defined(PLATFORM_WINDOWS_PC64) |
|
const char *pRead = (char *)&vertex; |
|
char *pCurrPos = (char *)m_pCurrPosition; |
|
__m128 m1 = _mm_load_ps( (float *)pRead ); |
|
__m128 m2 = _mm_load_ps( (float *)((intp)pRead + 16) ); |
|
__m128 m3 = _mm_load_ps( (float *)((intp)pRead + 32) ); |
|
_mm_stream_ps( (float *)pCurrPos, m1 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 16), m2 ); |
|
_mm_stream_ps( (float *)((intp)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 ) && !defined( PLATFORM_64BITS ) |
|
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 |
|
|
|
} |
|
#elif defined(__arm__) || defined(PLATFORM_WINDOWS_PC64) |
|
const void *pReadA = &vtx_a; |
|
const void *pReadB = &vtx_b; |
|
const void *pReadC = &vtx_c; |
|
const void *pReadD = &vtx_d; |
|
|
|
void *pCurrPos = m_pCurrPosition; |
|
|
|
__m128 m1 = _mm_load_ps( (float *)pReadA ); |
|
__m128 m2 = _mm_load_ps( (float *)((intp)pReadA + 16) ); |
|
__m128 m3 = _mm_load_ps( (float *)((intp)pReadA + 32) ); |
|
__m128 m4 = _mm_load_ps( (float *)pReadB ); |
|
__m128 m5 = _mm_load_ps( (float *)((intp)pReadB + 16) ); |
|
__m128 m6 = _mm_load_ps( (float *)((intp)pReadB + 32) ); |
|
_mm_stream_ps( (float *)pCurrPos, m1 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 16), m2 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 32), m3 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 48), m4 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 64), m5 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 80), m6 ); |
|
m1 = _mm_load_ps( (float *)pReadC ); |
|
m2 = _mm_load_ps( (float *)((intp)pReadC + 16) ); |
|
m3 = _mm_load_ps( (float *)((intp)pReadC + 32) ); |
|
m4 = _mm_load_ps( (float *)pReadD ); |
|
m5 = _mm_load_ps( (float *)((intp)pReadD + 16) ); |
|
m6 = _mm_load_ps( (float *)((intp)pReadD + 32) ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 0+96), m1 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 16+96), m2 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 32+96), m3 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 48+96), m4 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 64+96), m5 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 80+96), m6 ); |
|
#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 ) |
|
{ |
|
#if defined(__arm__) || defined(__aarch64__) || defined(PLATFORM_WINDOWS_PC64) |
|
FastVertexSSE( vertex ); |
|
#else |
|
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 |
|
#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 ) && !defined( PLATFORM_64BITS ) |
|
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) || defined(PLATFORM_WINDOWS_PC64) |
|
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"); */ |
|
|
|
__m128 m1 = _mm_load_ps( (float *)pRead ); |
|
__m128 m2 = _mm_load_ps( (float *)((intp)pRead + 16) ); |
|
__m128 m3 = _mm_load_ps( (float *)((intp)pRead + 32) ); |
|
__m128 m4 = _mm_load_ps( (float *)((intp)pRead + 48) ); |
|
_mm_stream_ps( (float *)pCurrPos, m1 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 16), m2 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 32), m3 ); |
|
_mm_stream_ps( (float *)((intp)pCurrPos + 48), m4 ); |
|
#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
|
|
|