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