Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

321 lines
13 KiB

//=========== Copyright © Valve Corporation, All rights reserved. ===========//
//
// Purpose: Mesh types for meshutils library
//
//===========================================================================//
#ifndef MESH_H
#define MESH_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlvector.h"
#include "tier1/utllinkedlist.h"
#include "mathlib/vector.h"
#include "materialsystem/imaterial.h"
#include "rendersystem/irenderdevice.h"
#include "bitvec.h"
class CDmeModel;
class CDmeMesh;
#define VERTEX_ELEMENT_TANGENT_WITH_FLIP VERTEX_ELEMENT_TEXCOORD4D_7
// our usual distance epsilon is 1/32 of an inch
const float ONE_32ND_UNIT = 0.03125f;
// UVChart for unique parameterization
struct UVChart_t
{
Vector4D m_vPlane;
CUtlVector<int> m_TriangleList;
Vector2D m_vMinUV;
Vector2D m_vMaxUV;
int m_nVertexStart;
int m_nVertexCount;
};
// Atlas chart data for atlasing
// TODO: we should probably pull atlasing out into a more visible library
struct AtlasChart_t
{
Vector2D m_vMaxTextureSize;
Vector2D m_vAtlasMin;
Vector2D m_vAtlasMax;
bool m_bAtlased;
};
// the mesh library only supports a single stream, with multiple attributes.
// NOTE: Each attribute must be an array of floats
class CMeshVertexAttribute
{
public:
int m_nOffsetFloats; // Offset is in # of floats
VertexElement_t m_nType;
inline bool IsJointWeight() const // { return !V_stricmp( m_name.Get(), "blendweight" ) || !V_stricmp( m_name.Get(), "blendweights" ); }
{
return m_nType == VERTEX_ELEMENT_BONEWEIGHTS1 || m_nType == VERTEX_ELEMENT_BONEWEIGHTS2 || m_nType == VERTEX_ELEMENT_BONEWEIGHTS3 || m_nType == VERTEX_ELEMENT_BONEWEIGHTS4;
}
inline bool IsJointIndices() const //{ return !V_stricmp( m_name.Get(), "blendindices" ); }
{
return m_nType == VERTEX_ELEMENT_BONEINDEX;
}
inline bool IsClothEnable()const //{ return !V_stricmp( m_name.Get(), "cloth_enable" ); }
{
return false; // needs to be string-based
}
inline bool IsPositionRemap()const //{ return !V_stricmp( m_name.Get(), "dme_position_map" ); }
{
return false; // needs to be string-based
}
};
class CMesh
{
public:
CMesh();
~CMesh();
// -----------------------------------------------------------------------------------------
// Allocate vertex and index space for a mesh, this memory will automatically be freed in the destructor
void AllocateMesh( int nVertexCount, int nIndexCount, int nVertexStride, CMeshVertexAttribute *pAttributes, int nAtrributeCount );
void AllocateAndCopyMesh( int nInputVertexCount, const float *pInputVerts, int nInputIndexCount, const uint32 *pInputIndices, int nVertexStride, CMeshVertexAttribute *pAttributes, int nAtrributeCount );
void FreeAllMemory();
// -----------------------------------------------------------------------------------------
// If you don't supply the attribute definition it assumes the vertices are just a position vector
// NOTE: VertexStrideFloats is 3 for a normal vector of 3 floats
// External meshes are allocated by external code and not freed in the destructor
void InitExternalMesh( float *pVerts, int nVertexCount, uint32 *pIndices, int nIndexCount, int nVertexStrideFloats = 3, CMeshVertexAttribute *pAttributes = NULL, int nAtrributeCount = 0 );
inline int IndexCount() const
{
return m_nIndexCount;
}
inline int VertexCount() const
{
return m_nVertexCount;
}
inline void SetVertexPosition( int nIndex, const Vector & v )
{
*( Vector * )GetVertex( nIndex ) = v ;
}
struct SkinningDataFields_t
{
SkinningDataFields_t ():m_nBoneWeights( -1 ), m_nBoneIndices( -1 ){}
bool HasSkinningData() const { return m_nBoneIndices >= 0; }
int m_nBoneWeights, m_nBoneIndices;
};
struct ClothDataFields_t
{
ClothDataFields_t() : m_nClothEnable( -1 ), m_nPositionRemap( -1 ) {}
bool HasClothData() const { return m_nClothEnable >= 0 && m_nPositionRemap >= 0; }
int m_nClothEnable, m_nPositionRemap;
};
// accessing a single attribute of a single vertex (attribute may be scalar or an array of T; scalar and array of size 1 are one and the same)
template <typename T>
class CSingleVertexFieldAccessor
{
public:
CSingleVertexFieldAccessor( T*pBase, int nCount ) : m_pBase( pBase ), m_nCount( nCount ) {}
protected:
T *m_pBase;
int m_nCount;
public:
T& operator [] ( int i ) { AssertDbg( i < m_nCount ); return m_pBase[ i ]; }
const T& operator [] ( int i ) const { AssertDbg( i < m_nCount ); return m_pBase[ i ]; }
T& operator *( ){ AssertDbg( m_nCount > 0 ); return *m_pBase; }
const T& operator *( ) const { AssertDbg( m_nCount > 0 ); return *m_pBase; }
T& Tail() { AssertDbg( m_nCount > 0 ); return m_pBase[ m_nCount - 1 ]; }
const T& Tail() const { AssertDbg( m_nCount > 0 ); return m_pBase[ m_nCount - 1 ]; }
int Count()const { return m_nCount; }
};
template <typename T>
class CScalarAttrArray;
// an accessor for a common attribute of all vertices (e.g. blendweights, or position, or normal)
template < typename T >
class CAttrArray
{
protected:
friend class CScalarAttrArray < T > ;
float *m_pBase; // this points to the first vertex field (first vertex + field offset)
int m_nStride; // this is the stride between fields
int m_nElementCount;
int m_nAttrCount;
public:
CAttrArray( float *pBase = NULL, int nStride = 0, int nVertexCount = 0, int nAttrCount = 0 )
: m_pBase( pBase )
, m_nStride( nStride )
, m_nElementCount( nVertexCount )
, m_nAttrCount( nAttrCount )
{ }
struct Range_t
{
T m_Min;
T m_Max;
Range_t();
void Append( T val )
{
if ( val < m_Min )
m_Min = val;
if ( val > m_Max )
m_Max = val;
}
};
Range_t GetRange()const
{
Range_t range;
for ( int v = 0; v < m_nElementCount; ++v )
{
const T *pValues = ( const T* )( m_pBase + m_nStride * v );
for ( int na = 0; na < m_nAttrCount; ++na )
{
range.Append( pValues[ na ] );
}
}
return range;
}
public:
// converting to bool to use this in if() to check for NULL
operator bool() const { return m_pBase != NULL; }
CSingleVertexFieldAccessor< T > operator []( int nVertex ) { AssertDbg( nVertex < m_nElementCount ); return CSingleVertexFieldAccessor< T >( ( T* )( m_pBase + m_nStride * nVertex ), m_nAttrCount ); }
CSingleVertexFieldAccessor< const T > operator []( int nVertex ) const { AssertDbg( nVertex < m_nElementCount ); return CSingleVertexFieldAccessor< const T >( ( const T* )( m_pBase + m_nStride * nVertex ), m_nAttrCount ); }
int GetElementCount()const { return m_nElementCount; }
int GetAttrCount()const { return m_nAttrCount; }
};
bool HasSkinningData() const;
SkinningDataFields_t GetSkinningDataFields() const;
ClothDataFields_t GetClothDataFields() const;
float GetVertexJointSumWeight( const SkinningDataFields_t &skinData, int nVertex, const CVarBitVec &jointSet ); // get the weight of this vertex at this joint; 1.0 for vertices rigdly bound to the given bone, 0.0 for vertices not bound to this bone
void AppendMesh( const CMesh &inputMesh );
inline int TriangleCount() const { return m_nIndexCount / 3; }
inline float *GetVertex(int nIndex) { return m_pVerts + (nIndex * m_nVertexStrideFloats); }
inline const float *GetVertex(int nIndex) const { return m_pVerts + (nIndex * m_nVertexStrideFloats); }
inline const Vector &GetVertexPosition(int nIndex) const { return *(Vector *)(m_pVerts + (nIndex * m_nVertexStrideFloats)); }
inline size_t GetVertexSizeInBytes() const { return m_nVertexStrideFloats * sizeof(float); }
inline size_t GetTotalVertexSizeInBytes() const { return GetVertexSizeInBytes() * m_nVertexCount; }
inline size_t GetTotalIndexSizeInBytes() const { return sizeof(uint32) * m_nIndexCount; }
Vector4D PlaneFromTriangle( int nTriangle ) const;
bool CalculateBounds( Vector *pMinOut, Vector *pMaxOut, int nStartVertex = 0, int nVertexCount = 0 ) const;
bool CalculateAdjacency( int *pAdjacencyOut, int nSizeAdjacencyOut ) const;
bool CalculateIndicentFacesForVertices( CUtlLinkedList<int> *pFacesPerVertex, int nFacesPerVertexSize ) const;
bool CalculateInputLayoutFromAttributes( RenderInputLayoutField_t *pOutFields, int *pInOutNumFields ) const;
int FindFirstAttributeOffset( VertexElement_t nType ) const;
void AddAttributes( CMeshVertexAttribute *pAttributes, int nAttributeCount );
void CalculateTangents();
bool CalculateTangentSpaceWorldLengthsPerFace( Vector2D *pLengthsOut, int nLengthsOut, float flMaxWorldPerUV = 256.0f );
bool CalculateFaceCenters( Vector *pCentersOut, int nCentersOut );
int GetAttrSizeFloats( int nAttribute );
float *m_pVerts;
CMeshVertexAttribute *m_pAttributes;
uint32 *m_pIndices;
int m_nVertexCount;
int m_nVertexStrideFloats; // Stride is in # of floats
int m_nAttributeCount;
int m_nIndexCount;
CUtlString m_materialName;
private:
void RestrideVertexBuffer( int nNewStrideFloats );
bool m_bAllocatedMeshData;
};
struct GridVolume_t
{
Vector m_vMinBounds;
Vector m_vMaxBounds;
};
struct IndexRange_t
{
int m_nStartIndex;
int m_nIndexCount;
};
bool ClipMeshToHalfSpace( CMesh *pMeshBack, CMesh *pMeshFront, const CMesh &inputMesh, Vector4D &vClipPlane );
void DuplicateMesh( CMesh *pMeshOut, const CMesh &inputMesh );
bool RationalizeUVsInPlace( CMesh *pMesh );
bool RationalizeUVs( CMesh *pRationalMeshOut, const CMesh &inputMesh );
void DeIndexMesh( CMesh *pMeshOut, const CMesh &inputMesh );
bool ConcatMeshes( CMesh *pMeshOut, CMesh **ppMeshIn, int nInputMeshes,
CMeshVertexAttribute *pAttributeOverride = NULL, int nAttributeOverrideCount = 0, int nStrideOverride = 0 );
bool TessellateOnWrappedUV( CMesh *pMeshOut, const CMesh &inputMesh );
// returns the mesh containing the convex hull of the input mesh
void ConvexHull3D( CMesh *pOutMesh, const CMesh &inputMesh, float flCoplanarEpsilon = ONE_32ND_UNIT );
void FitOBBToMesh( matrix3x4_t *pCenter, Vector *pExtents, const CMesh &inputMesh, float flCoplanarEpsilon = ONE_32ND_UNIT );
// Builds a mesh of the hull defined by an array of planes (pointing out of the solid - plane eq is Ax + By + Cz = D). Optionally, outputs the index of the originating input plane for each triangle in the mesh.
void HullFromPlanes( CMesh *pOutMesh, CUtlVector<uint32> *pTrianglePlaneIndex, const float *pPlanes, int nPlaneCount, int nPlaneStrideFloats, float flCoplanarEpsilon = ONE_32ND_UNIT );
// Same as above but work on a temporary list of planes described as fltx4 (the buffer will be modified in place). No stride needed in this case, ABCD = XYZW
void HullFromPlanes_SIMD( CMesh *pOutMesh, CUtlVector<uint16> *pTrianglePlaneIndex, fltx4 *pPlanes, int nPlaneCount, float flCoplanarEpsilon = ONE_32ND_UNIT );
// TODO: Extreme welds can cause faces to flip/invert. Should we add an option to detect and avoid this?
bool WeldVertices( CMesh *pMeshOut, const CMesh &inputMesh, float *pEpsilons, int nEpsilons );
// This removes any degenerate triangles and puts the vertices in access order
void CleanMesh( CMesh *pMeshOut, const CMesh &inputMesh );
// partitions the mesh into an array of meshes, each with <= nMaxVertex vertices
void SplitMesh( CUtlVector<CMesh> &list, const CMesh &input, int nMaxVertex );
// Create a unique parameterization of the mesh
bool CreateUniqueUVParameterization( CMesh *pMeshOut, const CMesh &inputMesh, float flFaceAngleThreshold, int nAtlasTextureSizeX, int nAtlasTextureSizeY, float flGutterSize );
// Pack a set of charts into an atlas
int PackChartsIntoAtlas( AtlasChart_t *pCharts, int nCharts, int nAtlasTextureSizeX, int nAtlasTextureSizeY, int nAtlasGrow );
// Creates a list of AABBs for a give volume given a target AABB size
void CreateGridCellsForVolume( CUtlVector< GridVolume_t > &outputVolumes, const Vector &vMinBounds, const Vector &vMaxBounds, const Vector &vGridSize );
//--------------------------------------------------------------------------------------
// For a list of AABBs, find all indices in the mesh that belong to each AABB. Then
// coalesce those indices into a single buffer in order of the input AABBs and return a
// list of ranges of the indices in the output mesh that are needed in each input volume
//--------------------------------------------------------------------------------------
void CreatedGriddedIndexRangesFromMesh( CMesh *pOutputMesh, CUtlVector< IndexRange_t > &outputRanges, const CMesh &inputMesh, CUtlVector< GridVolume_t > &inputVolumes );
// Per-vertex ops
void CopyVertex( float *pOut, const float *pIn, int nFloats );
void SubtractVertex( float *pOutput, const float *pLeft, const float *pRight, int nFloats );
void AddVertex( float *pOutput, const float *pLeft, const float *pRight, int nFloats );
void MultiplyVertex( float *pOutput, const float *pLeft, const float* pRight, float nFloats );
void AddVertexInPlace( float *pLeft, const float *pRight, int nFloats );
void MultiplyVertexInPlace( float *pLeft, float flRight, int nFloats );
void LerpVertex( float *pOutput, const float *pLeft, const float *pRight, float flLerp, int nFloats );
void BaryCentricVertices( float *pOutput, float *p0, float *p1, float *p2, float flU, float flV, float flW, int nFloats );
// simple hashing function for edges
inline uint32 VertHashKey( int nV0, int nV1 )
{
uint32 nHash = ((uint32(nV0) >> 16) | (uint32(nV0)<<16)) ^ uint32(nV1);
nHash = ieqsel( nHash, uint32(~0), uint32( 0 ), nHash ); // don't ever return ~0
return nHash;
}
#endif // MESH_H