summaryrefslogtreecommitdiff
path: root/movieobjects/dmemesh.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'movieobjects/dmemesh.cpp')
-rw-r--r--movieobjects/dmemesh.cpp5017
1 files changed, 5017 insertions, 0 deletions
diff --git a/movieobjects/dmemesh.cpp b/movieobjects/dmemesh.cpp
new file mode 100644
index 0000000..aac63a9
--- /dev/null
+++ b/movieobjects/dmemesh.cpp
@@ -0,0 +1,5017 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+// Standard includes
+#include <limits.h>
+
+// Valve includes
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmevertexdata.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmetransform.h"
+#include "movieobjects/dmemodel.h"
+#include "movieobjects_interfaces.h"
+#include "movieobjects/dmecombinationoperator.h"
+#include "movieobjects/dmeselection.h"
+#include "movieobjects/dmedrawsettings.h"
+#include "movieobjects/dmmeshcomp.h"
+#include "tier3/tier3.h"
+#include "tier1/KeyValues.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imorph.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/imaterialvar.h"
+#include "istudiorender.h"
+#include "studio.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Normal rendering materials
+//-----------------------------------------------------------------------------
+bool CDmeMesh::s_bNormalMaterialInitialized;
+CMaterialReference CDmeMesh::s_NormalMaterial;
+CMaterialReference CDmeMesh::s_NormalErrorMaterial;
+
+
+
+//-----------------------------------------------------------------------------
+// Computes a skin matrix
+//-----------------------------------------------------------------------------
+static const matrix3x4_t *ComputeSkinMatrix( int nBoneCount, const float *pJointWeight, const int *pJointIndices, const matrix3x4_t *pPoseToWorld, matrix3x4_t &result )
+{
+ float flWeight0, flWeight1, flWeight2, flWeight3;
+
+ switch( nBoneCount )
+ {
+ default:
+ case 1:
+ return &pPoseToWorld[pJointIndices[0]];
+
+ case 2:
+ {
+ const matrix3x4_t &boneMat0 = pPoseToWorld[pJointIndices[0]];
+ const matrix3x4_t &boneMat1 = pPoseToWorld[pJointIndices[1]];
+ flWeight0 = pJointWeight[0];
+ flWeight1 = pJointWeight[1];
+
+ // NOTE: Inlining here seems to make a fair amount of difference
+ result[0][0] = boneMat0[0][0] * flWeight0 + boneMat1[0][0] * flWeight1;
+ result[0][1] = boneMat0[0][1] * flWeight0 + boneMat1[0][1] * flWeight1;
+ result[0][2] = boneMat0[0][2] * flWeight0 + boneMat1[0][2] * flWeight1;
+ result[0][3] = boneMat0[0][3] * flWeight0 + boneMat1[0][3] * flWeight1;
+ result[1][0] = boneMat0[1][0] * flWeight0 + boneMat1[1][0] * flWeight1;
+ result[1][1] = boneMat0[1][1] * flWeight0 + boneMat1[1][1] * flWeight1;
+ result[1][2] = boneMat0[1][2] * flWeight0 + boneMat1[1][2] * flWeight1;
+ result[1][3] = boneMat0[1][3] * flWeight0 + boneMat1[1][3] * flWeight1;
+ result[2][0] = boneMat0[2][0] * flWeight0 + boneMat1[2][0] * flWeight1;
+ result[2][1] = boneMat0[2][1] * flWeight0 + boneMat1[2][1] * flWeight1;
+ result[2][2] = boneMat0[2][2] * flWeight0 + boneMat1[2][2] * flWeight1;
+ result[2][3] = boneMat0[2][3] * flWeight0 + boneMat1[2][3] * flWeight1;
+ }
+ return &result;
+
+ case 3:
+ {
+ const matrix3x4_t &boneMat0 = pPoseToWorld[pJointIndices[0]];
+ const matrix3x4_t &boneMat1 = pPoseToWorld[pJointIndices[1]];
+ const matrix3x4_t &boneMat2 = pPoseToWorld[pJointIndices[2]];
+ flWeight0 = pJointWeight[0];
+ flWeight1 = pJointWeight[1];
+ flWeight2 = pJointWeight[2];
+
+ result[0][0] = boneMat0[0][0] * flWeight0 + boneMat1[0][0] * flWeight1 + boneMat2[0][0] * flWeight2;
+ result[0][1] = boneMat0[0][1] * flWeight0 + boneMat1[0][1] * flWeight1 + boneMat2[0][1] * flWeight2;
+ result[0][2] = boneMat0[0][2] * flWeight0 + boneMat1[0][2] * flWeight1 + boneMat2[0][2] * flWeight2;
+ result[0][3] = boneMat0[0][3] * flWeight0 + boneMat1[0][3] * flWeight1 + boneMat2[0][3] * flWeight2;
+ result[1][0] = boneMat0[1][0] * flWeight0 + boneMat1[1][0] * flWeight1 + boneMat2[1][0] * flWeight2;
+ result[1][1] = boneMat0[1][1] * flWeight0 + boneMat1[1][1] * flWeight1 + boneMat2[1][1] * flWeight2;
+ result[1][2] = boneMat0[1][2] * flWeight0 + boneMat1[1][2] * flWeight1 + boneMat2[1][2] * flWeight2;
+ result[1][3] = boneMat0[1][3] * flWeight0 + boneMat1[1][3] * flWeight1 + boneMat2[1][3] * flWeight2;
+ result[2][0] = boneMat0[2][0] * flWeight0 + boneMat1[2][0] * flWeight1 + boneMat2[2][0] * flWeight2;
+ result[2][1] = boneMat0[2][1] * flWeight0 + boneMat1[2][1] * flWeight1 + boneMat2[2][1] * flWeight2;
+ result[2][2] = boneMat0[2][2] * flWeight0 + boneMat1[2][2] * flWeight1 + boneMat2[2][2] * flWeight2;
+ result[2][3] = boneMat0[2][3] * flWeight0 + boneMat1[2][3] * flWeight1 + boneMat2[2][3] * flWeight2;
+ }
+ return &result;
+
+ case 4:
+ {
+ const matrix3x4_t &boneMat0 = pPoseToWorld[pJointIndices[0]];
+ const matrix3x4_t &boneMat1 = pPoseToWorld[pJointIndices[1]];
+ const matrix3x4_t &boneMat2 = pPoseToWorld[pJointIndices[2]];
+ const matrix3x4_t &boneMat3 = pPoseToWorld[pJointIndices[3]];
+ flWeight0 = pJointWeight[0];
+ flWeight1 = pJointWeight[1];
+ flWeight2 = pJointWeight[2];
+ flWeight3 = pJointWeight[3];
+
+ result[0][0] = boneMat0[0][0] * flWeight0 + boneMat1[0][0] * flWeight1 + boneMat2[0][0] * flWeight2 + boneMat3[0][0] * flWeight3;
+ result[0][1] = boneMat0[0][1] * flWeight0 + boneMat1[0][1] * flWeight1 + boneMat2[0][1] * flWeight2 + boneMat3[0][1] * flWeight3;
+ result[0][2] = boneMat0[0][2] * flWeight0 + boneMat1[0][2] * flWeight1 + boneMat2[0][2] * flWeight2 + boneMat3[0][2] * flWeight3;
+ result[0][3] = boneMat0[0][3] * flWeight0 + boneMat1[0][3] * flWeight1 + boneMat2[0][3] * flWeight2 + boneMat3[0][3] * flWeight3;
+ result[1][0] = boneMat0[1][0] * flWeight0 + boneMat1[1][0] * flWeight1 + boneMat2[1][0] * flWeight2 + boneMat3[1][0] * flWeight3;
+ result[1][1] = boneMat0[1][1] * flWeight0 + boneMat1[1][1] * flWeight1 + boneMat2[1][1] * flWeight2 + boneMat3[1][1] * flWeight3;
+ result[1][2] = boneMat0[1][2] * flWeight0 + boneMat1[1][2] * flWeight1 + boneMat2[1][2] * flWeight2 + boneMat3[1][2] * flWeight3;
+ result[1][3] = boneMat0[1][3] * flWeight0 + boneMat1[1][3] * flWeight1 + boneMat2[1][3] * flWeight2 + boneMat3[1][3] * flWeight3;
+ result[2][0] = boneMat0[2][0] * flWeight0 + boneMat1[2][0] * flWeight1 + boneMat2[2][0] * flWeight2 + boneMat3[2][0] * flWeight3;
+ result[2][1] = boneMat0[2][1] * flWeight0 + boneMat1[2][1] * flWeight1 + boneMat2[2][1] * flWeight2 + boneMat3[2][1] * flWeight3;
+ result[2][2] = boneMat0[2][2] * flWeight0 + boneMat1[2][2] * flWeight1 + boneMat2[2][2] * flWeight2 + boneMat3[2][2] * flWeight3;
+ result[2][3] = boneMat0[2][3] * flWeight0 + boneMat1[2][3] * flWeight1 + boneMat2[2][3] * flWeight2 + boneMat3[2][3] * flWeight3;
+ }
+ return &result;
+ }
+
+ Assert(0);
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Helper class to deal with software skinning
+//-----------------------------------------------------------------------------
+class CRenderInfo
+{
+public:
+ CRenderInfo( const CDmeVertexData *pBaseState );
+
+ void ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, CDmeMesh::RenderVertexDelta_t *pDelta, Vector *pPosition, Vector *pNormal, Vector4D *pTangent );
+ void ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, int nStride, Vector *pPosition );
+ void ComputePosition( int posIndex, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, Vector *pPosition );
+
+ inline bool HasPositionData() const { return m_bHasPositionData; }
+ inline bool HasNormalData() const { return m_bHasNormalData; }
+ inline bool HasTangentData() const { return m_bHasTangentData; }
+private:
+ const CUtlVector<int>& m_PositionIndices;
+ const CUtlVector<Vector>& m_PositionData;
+ const CUtlVector<int>& m_NormalIndices;
+ const CUtlVector<Vector>& m_NormalData;
+ const CUtlVector<int>& m_TangentIndices;
+ const CUtlVector<Vector4D>& m_TangentData;
+ const CDmeVertexData *m_pBaseState;
+ int m_nJointCount;
+ bool m_bHasPositionData;
+ bool m_bHasNormalData;
+ bool m_bHasTangentData;
+ bool m_bHasSkinningData;
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CRenderInfo::CRenderInfo( const CDmeVertexData *pBaseState ) :
+ m_PositionIndices( pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) ),
+ m_PositionData( pBaseState->GetPositionData() ),
+ m_NormalIndices( pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_NORMAL ) ),
+ m_NormalData( pBaseState->GetNormalData() ),
+ m_TangentIndices( pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_TANGENT ) ),
+ m_TangentData( pBaseState->GetTangentData() )
+{
+ m_pBaseState = pBaseState;
+ m_bHasPositionData = m_PositionIndices.Count() > 0;
+ m_bHasNormalData = m_NormalIndices.Count() > 0;
+ m_bHasTangentData = m_TangentIndices.Count() > 0;
+ m_nJointCount = pBaseState->JointCount();
+ m_bHasSkinningData = pBaseState->HasSkinningData() && m_nJointCount > 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes where a vertex is
+//-----------------------------------------------------------------------------
+void CRenderInfo::ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, int nDeltaStride, Vector *pPosition )
+{
+ matrix3x4_t result;
+ Vector vecMorphPosition, vecMorphNormal;
+ const matrix3x4_t *pSkinMatrix = pPoseToWorld;
+ if ( m_bHasSkinningData )
+ {
+ const float *pJointWeight = m_pBaseState->GetJointWeights( vi );
+ const int *pJointIndices = m_pBaseState->GetJointIndices( vi );
+ pSkinMatrix = ComputeSkinMatrix( m_nJointCount, pJointWeight, pJointIndices, pPoseToWorld, result );
+ }
+
+ int pi = m_PositionIndices[ vi ];
+ const Vector *pPositionData = &m_PositionData[ pi ];
+ if ( pDeltaPosition )
+ {
+ Vector *pDelta = (Vector*)( (unsigned char *)pDeltaPosition + nDeltaStride * pi );
+ VectorAdd( *pPositionData, *pDelta, vecMorphPosition );
+ pPositionData = &vecMorphPosition;
+ }
+ VectorTransform( *pPositionData, *pSkinMatrix, *pPosition );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes where a vertex is
+//-----------------------------------------------------------------------------
+void CRenderInfo::ComputePosition( int posIndex, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, Vector *pPosition )
+{
+ matrix3x4_t result;
+ Vector vecMorphPosition;
+ const matrix3x4_t *pSkinMatrix = pPoseToWorld;
+
+ if ( m_bHasSkinningData )
+ {
+ const float *pJointWeight = m_pBaseState->GetJointPositionWeights( posIndex );
+ const int *pJointIndices = m_pBaseState->GetJointPositionIndices( posIndex );
+ pSkinMatrix = ComputeSkinMatrix( m_nJointCount, pJointWeight, pJointIndices, pPoseToWorld, result );
+ }
+
+ const Vector *pPositionData = &m_PositionData[ posIndex ];
+
+ if ( pDeltaPosition )
+ {
+ VectorAdd( *pPositionData, *( pDeltaPosition + posIndex ), vecMorphPosition );
+ pPositionData = &vecMorphPosition;
+ }
+
+ VectorTransform( *pPositionData, *pSkinMatrix, *( pPosition + posIndex ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes where a vertex is
+//-----------------------------------------------------------------------------
+void CRenderInfo::ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, CDmeMesh::RenderVertexDelta_t *pDelta, Vector *pPosition, Vector *pNormal, Vector4D *pTangent )
+{
+ matrix3x4_t result;
+ Vector vecMorphPosition, vecMorphNormal;
+ const matrix3x4_t *pSkinMatrix = pPoseToWorld;
+
+ if ( m_bHasSkinningData )
+ {
+ const float *pJointWeight = m_pBaseState->GetJointWeights( vi );
+ const int *pJointIndices = m_pBaseState->GetJointIndices( vi );
+ pSkinMatrix = ComputeSkinMatrix( m_nJointCount, pJointWeight, pJointIndices, pPoseToWorld, result );
+ }
+
+ int pi = m_PositionIndices[ vi ];
+ const Vector *pPositionData = &m_PositionData[ pi ];
+ if ( pDelta )
+ {
+ VectorAdd( *pPositionData, pDelta[ pi ].m_vecDeltaPosition, vecMorphPosition );
+ pPositionData = &vecMorphPosition;
+ }
+ VectorTransform( *pPositionData, *pSkinMatrix, *pPosition );
+
+ if ( m_bHasNormalData )
+ {
+ int ni = m_NormalIndices[ vi ];
+ const Vector *pNormalData = &m_NormalData[ ni ];
+ if ( pDelta )
+ {
+ VectorAdd( *pNormalData, pDelta[ni].m_vecDeltaNormal, vecMorphNormal );
+ pNormalData = &vecMorphNormal;
+ }
+ VectorRotate( *pNormalData, *pSkinMatrix, *pNormal );
+ VectorNormalize( *pNormal );
+ }
+ else
+ {
+ pNormal->Init( 0.0f, 0.0f, 1.0f );
+ }
+
+ if ( m_bHasTangentData )
+ {
+ const Vector4D &tangentData = m_TangentData[ m_TangentIndices[ vi ] ];
+ VectorRotate( tangentData.AsVector3D(), *pSkinMatrix, pTangent->AsVector3D() );
+ VectorNormalize( pTangent->AsVector3D() );
+ pTangent->w = tangentData.w;
+ }
+ else
+ {
+ pTangent->Init( 1.0f, 0.0f, 0.0f, 1.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMesh, CDmeMesh );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMesh::OnConstruction()
+{
+ m_BindBaseState.Init( this, "bindState" );
+ m_CurrentBaseState.Init( this, "currentState" );
+ m_BaseStates.Init( this, "baseStates", FATTRIB_MUSTCOPY );
+ m_DeltaStates.Init( this, "deltaStates", FATTRIB_MUSTCOPY | FATTRIB_HAS_CALLBACK );
+ m_FaceSets.Init( this, "faceSets", FATTRIB_MUSTCOPY );
+ m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Init( this, "deltaStateWeights" );
+ m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED].Init( this, "deltaStateWeightsLagged" );
+}
+
+void CDmeMesh::OnDestruction()
+{
+ if ( g_pMaterialSystem )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ int nCount = m_hwFaceSets.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !m_hwFaceSets[i].m_bBuilt )
+ continue;
+
+ if ( m_hwFaceSets[i].m_pMesh )
+ {
+ pRenderContext->DestroyStaticMesh( m_hwFaceSets[i].m_pMesh );
+ }
+ }
+ m_hwFaceSets.RemoveAll();
+ }
+
+ DeleteAttributeVarElementArray( m_BaseStates );
+ DeleteAttributeVarElementArray( m_DeltaStates );
+ DeleteAttributeVarElementArray( m_FaceSets );
+}
+
+
+//-----------------------------------------------------------------------------
+// Initializes the normal material
+//-----------------------------------------------------------------------------
+void CDmeMesh::InitializeNormalMaterial()
+{
+ if ( !s_bNormalMaterialInitialized )
+ {
+ s_bNormalMaterialInitialized = true;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$decal", 1 );
+// pVMTKeyValues->SetInt( "$ignorez", 0 );
+ s_NormalMaterial.Init( "__DmeMeshNormalMaterial", pVMTKeyValues );
+
+ pVMTKeyValues = new KeyValues( "unlitgeneric" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ s_NormalErrorMaterial.Init( "__DmeMeshNormalErrorMaterial", pVMTKeyValues );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resolve internal data from changed attributes
+//-----------------------------------------------------------------------------
+void CDmeMesh::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+
+ if ( pAttribute == m_DeltaStates.GetAttribute() )
+ {
+ int nDeltaStateCount = m_DeltaStates.Count();
+ for ( int i = 0; i < MESH_DELTA_WEIGHT_TYPE_COUNT; ++i )
+ {
+ // Make sure we have the correct number of weights
+ int nWeightCount = m_DeltaStateWeights[i].Count();
+ if ( nWeightCount < nDeltaStateCount )
+ {
+ for ( int j = nWeightCount; j < nDeltaStateCount; ++j )
+ {
+ m_DeltaStateWeights[i].AddToTail( Vector2D( 0.0f, 0.0f ) );
+ }
+ }
+ else if ( nDeltaStateCount > nWeightCount )
+ {
+ m_DeltaStateWeights[i].RemoveMultiple( nWeightCount, nWeightCount - nDeltaStateCount );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds deltas into a delta mesh
+//-----------------------------------------------------------------------------
+template< class T > bool CDmeMesh::AddVertexDelta(
+ CDmeVertexData *pBaseState,
+ void *pVertexData, int nStride, CDmeVertexDataBase::StandardFields_t fieldId, int nIndex, bool bDoLag )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nIndex );
+
+ if ( !pBaseState || !pDeltaState )
+ return false;
+
+ const FieldIndex_t nBaseFieldIndex = pBaseState->FindFieldIndex( fieldId == CDmeVertexData::FIELD_WRINKLE ? CDmeVertexData::FIELD_TEXCOORD : fieldId );
+ const FieldIndex_t nDeltaFieldIndex = pDeltaState->FindFieldIndex( fieldId );
+ if ( nBaseFieldIndex < 0 || nDeltaFieldIndex < 0 )
+ return false;
+
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nDeltaFieldIndex );
+ const CDmrArray<T> delta = pDeltaState->GetVertexData( nDeltaFieldIndex );
+ const int nDeltaCount = indices.Count();
+
+ const float flWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][nIndex].x;
+
+ const FieldIndex_t nSpeedFieldIndex = pBaseState->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED );
+
+ if ( !bDoLag || nSpeedFieldIndex < 0 )
+ {
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flWeight;
+ }
+
+ return true;
+ }
+
+ const float flLaggedWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][nIndex].x;
+
+ const CDmrArrayConst<int> speedIndices = pBaseState->GetIndexData( nSpeedFieldIndex );
+ const CDmrArrayConst<float> speedDelta = pBaseState->GetVertexData( nSpeedFieldIndex );
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ const CUtlVector<int> &list = pBaseState->FindVertexIndicesFromDataIndex( nBaseFieldIndex, nDataIndex );
+ Assert( list.Count() > 0 );
+ // FIXME: Average everything in the list.. shouldn't be necessary though
+ float flSpeed = speedDelta.Get( speedIndices.Get( list[0] ) );
+ float flActualWeight = Lerp( flSpeed, flLaggedWeight, flWeight );
+
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flActualWeight;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::AddTexCoordDelta( RenderVertexDelta_t *pRenderDelta, float flWeight, CDmeVertexDeltaData *pDeltaState )
+{
+ FieldIndex_t nFieldIndex = pDeltaState->FindFieldIndex( CDmeVertexDeltaData::FIELD_TEXCOORD );
+ if ( nFieldIndex < 0 )
+ return;
+
+ bool bIsVCoordinateFlipped = pDeltaState->IsVCoordinateFlipped();
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nFieldIndex );
+ const CDmrArray<Vector2D> delta = pDeltaState->GetVertexData( nFieldIndex );
+ int nDeltaCount = indices.Count();
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ Vector2D uvDelta = delta.Get( j );
+ if ( bIsVCoordinateFlipped )
+ {
+ uvDelta.y = -uvDelta.y;
+ }
+ Vector2D &vec2D = pRenderDelta[ indices.Get( j ) ].m_vecDeltaUV;
+ Vector2DMA( vec2D, flWeight, uvDelta, vec2D );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::AddColorDelta( RenderVertexDelta_t *pRenderDelta, float flWeight, CDmeVertexDeltaData *pDeltaState )
+{
+ FieldIndex_t nFieldIndex = pDeltaState->FindFieldIndex( CDmeVertexDeltaData::FIELD_COLOR );
+ if ( nFieldIndex < 0 )
+ return;
+
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nFieldIndex );
+ const CDmrArray<Color> delta = pDeltaState->GetVertexData( nFieldIndex );
+ int nDeltaCount = indices.Count();
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ const Color &srcDeltaColor = delta[ j ];
+ Vector4D &vecDelta = pRenderDelta[ indices[ j ] ].m_vecDeltaColor;
+ vecDelta[0] += flWeight * srcDeltaColor.r();
+ vecDelta[1] += flWeight * srcDeltaColor.g();
+ vecDelta[2] += flWeight * srcDeltaColor.b();
+ vecDelta[3] += flWeight * srcDeltaColor.a();
+ }
+}
+
+template< class T > bool CDmeMesh::AddStereoVertexDelta(
+ CDmeVertexData *pBaseState,
+ void *pVertexData, int nStride, CDmeVertexDataBase::StandardFields_t fieldId, int nIndex, bool bDoLag )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nIndex );
+ if ( !pBaseState || !pDeltaState )
+ return false;
+
+ const FieldIndex_t nBaseFieldIndex = pBaseState->FindFieldIndex( fieldId == CDmeVertexData::FIELD_WRINKLE ? CDmeVertexData::FIELD_TEXCOORD : fieldId );
+ const FieldIndex_t nDeltaFieldIndex = pDeltaState->FindFieldIndex( fieldId );
+ if ( nBaseFieldIndex < 0 || nDeltaFieldIndex < 0 )
+ return false;
+
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][nIndex].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][nIndex].y;
+
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nDeltaFieldIndex );
+ const CDmrArray<T> delta = pDeltaState->GetVertexData( nDeltaFieldIndex );
+ const CUtlVector<int>& balanceIndices = pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_BALANCE );
+ const CUtlVector<float> &balanceDelta = pBaseState->GetBalanceData();
+ const int nDeltaCount = indices.Count();
+
+ const FieldIndex_t nSpeedFieldIndex = pBaseState->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED );
+
+ if ( !bDoLag || nSpeedFieldIndex < 0 )
+ {
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ const CUtlVector<int> &list = pBaseState->FindVertexIndicesFromDataIndex( nBaseFieldIndex, nDataIndex );
+ Assert( list.Count() > 0 );
+ // FIXME: Average everything in the list.. shouldn't be necessary though
+ float flRightAmount = balanceDelta[ balanceIndices[ list[0] ] ];
+ float flWeight = Lerp( flRightAmount, flLeftWeight, flRightWeight );
+
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flWeight;
+ }
+
+ return true;
+ }
+
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][nIndex].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][nIndex].y;
+
+ const CDmrArray<int> pSpeedIndices = pBaseState->GetIndexData( nSpeedFieldIndex );
+ const CDmrArray<float> pSpeedDelta = pBaseState->GetVertexData( nSpeedFieldIndex );
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ const CUtlVector<int> &list = pBaseState->FindVertexIndicesFromDataIndex( nBaseFieldIndex, nDataIndex );
+ Assert( list.Count() > 0 );
+ // FIXME: Average everything in the list.. shouldn't be necessary though
+ float flRightAmount = balanceDelta[ balanceIndices[ list[0] ] ];
+ float flWeight = Lerp( flRightAmount, flLeftWeight, flRightWeight );
+ float flLaggedWeight = Lerp( flRightAmount, flLeftWeightLagged, flRightWeightLagged );
+ float flSpeed = pSpeedDelta.Get( pSpeedIndices.Get( list[0] ) );
+ float flActualWeight = Lerp( flSpeed, flLaggedWeight, flWeight );
+
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flActualWeight;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh when it uses too many bones
+//-----------------------------------------------------------------------------
+bool CDmeMesh::BuildDeltaMesh( int nVertices, RenderVertexDelta_t *pRenderDelta )
+{
+ bool bHasWrinkleDelta = false;
+
+ memset( pRenderDelta, 0, nVertices * sizeof( RenderVertexDelta_t ) );
+ int nCount = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count();
+ Assert( m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count() == m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED].Count() );
+
+ CDmeVertexData *pBindState = GetBindBaseState();
+
+ const FieldIndex_t nBalanceFieldIndex = pBindState->FindFieldIndex( CDmeVertexDeltaData::FIELD_BALANCE );
+ const FieldIndex_t nSpeedFieldIndex = pBindState->FindFieldIndex( CDmeVertexDeltaData::FIELD_MORPH_SPEED );
+ const bool bDoLag = nSpeedFieldIndex >= 0;
+ if ( nBalanceFieldIndex < 0 )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flLaggedWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ if ( flWeight <= 0.0f && flLaggedWeight <= 0.0f )
+ continue;
+
+ // prepare vertices
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState(i);
+ AddVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaPosition, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ AddVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaNormal, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_NORMAL, i, bDoLag );
+ AddTexCoordDelta( pRenderDelta, flWeight, pDeltaState );
+ AddColorDelta( pRenderDelta, flWeight, pDeltaState );
+ bool bWrinkle = AddVertexDelta<float>( pBindState, &pRenderDelta->m_flDeltaWrinkle, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_WRINKLE, i, bDoLag );
+ bHasWrinkleDelta = bHasWrinkleDelta || bWrinkle;
+ }
+
+ return bHasWrinkleDelta;
+ }
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].y;
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].y;
+ if ( flLeftWeight <= 0.0f && flRightWeight <= 0.0f && flLeftWeightLagged <= 0.0f && flRightWeightLagged <= 0.0f )
+ continue;
+
+ // FIXME: Need to make balanced versions of texcoord + color
+ bool bWrinkle;
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState(i);
+ AddStereoVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaPosition, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ AddStereoVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaNormal, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_NORMAL, i, bDoLag );
+ bWrinkle = AddStereoVertexDelta<float>( pBindState, &pRenderDelta->m_flDeltaWrinkle, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_WRINKLE, i, bDoLag);
+ bHasWrinkleDelta = bHasWrinkleDelta || bWrinkle;
+ AddTexCoordDelta( pRenderDelta, flLeftWeight, pDeltaState );
+ AddColorDelta( pRenderDelta, flLeftWeight, pDeltaState );
+ }
+
+ return bHasWrinkleDelta;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes triangulated indices for a face set into a meshbuilder
+//-----------------------------------------------------------------------------
+void CDmeMesh::WriteTriangluatedIndices( const CDmeVertexData *pBaseState, CDmeFaceSet *pFaceSet, CMeshBuilder &meshBuilder )
+{
+ // prepare indices
+ int nFirstIndex = 0;
+ int nIndexCount = pFaceSet->NumIndices();
+ while ( nFirstIndex < nIndexCount )
+ {
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+ if ( nVertexCount >= 3 )
+ {
+ int nOutCount = ( nVertexCount-2 ) * 3;
+ int *pIndices = (int*)_alloca( nOutCount * sizeof(int) );
+ ComputeTriangulatedIndices( pBaseState, pFaceSet, nFirstIndex, pIndices, nOutCount );
+ for ( int ii = 0; ii < nOutCount; ++ii )
+ {
+ meshBuilder.FastIndex( pIndices[ii] );
+ }
+ }
+ nFirstIndex += nVertexCount + 1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh when it uses too many bones
+//-----------------------------------------------------------------------------
+void CDmeMesh::DrawDynamicMesh( CDmeFaceSet *pFaceSet, matrix3x4_t *pPoseToWorld, bool bHasActiveDeltaStates, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ CDmeVertexData *pBindBase = GetCurrentBaseState();
+ if ( !pBindBase )
+ return;
+
+ // NOTE: This is inherently inefficient; we re-skin the *entire* mesh,
+ // even if it's not being used by the entire model. This is because we can't
+ // guarantee the various materials from the various face sets use the
+ // same vertex format (even though they should), and we don't want to
+ // spend the work to detemine the sub-part of the mesh used by this face set.
+
+ // Compute vertex deltas for rendering
+ const int nVertices = pBindBase->VertexCount();
+
+ // NOTE: The Delta Data is actually indexed by the pPositionIndices, pNormalIndices, etc.
+ // The fact that we're storing one delta per final vertex nVertices
+ // is a waste of memory and simply implementational convenience.
+ bool bHasActiveWrinkle = false;
+ RenderVertexDelta_t *pVertexDelta = (RenderVertexDelta_t*)_alloca( nVertices * sizeof(RenderVertexDelta_t) );
+ if ( bHasActiveDeltaStates )
+ {
+ bHasActiveWrinkle = BuildDeltaMesh( nVertices, pVertexDelta );
+ }
+ else
+ {
+ pVertexDelta = NULL;
+ }
+
+ CRenderInfo renderInfo( pBindBase );
+ Assert( renderInfo.HasPositionData() );
+
+ // prepare vertices
+ FieldIndex_t uvField = pBindBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ FieldIndex_t colorField = pBindBase->FindFieldIndex( CDmeVertexData::FIELD_COLOR );
+
+ bool bHasTexCoords = ( uvField >= 0 );
+ bool bHasColors = ( colorField >= 0 );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ const CDmrArrayConst<int> pUVIndices = bHasTexCoords ? pBindBase->GetIndexData( uvField ) : NULL;
+
+ if ( bHasActiveWrinkle && bHasTexCoords )
+ {
+ // Create the wrinkle flex mesh
+ IMesh *pFlexDelta = pRenderContext->GetFlexMesh();
+ int nFlexVertexOffset = 0;
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pFlexDelta, MATERIAL_HETEROGENOUS, nVertices, 0, &nFlexVertexOffset );
+ for ( int j=0; j < nVertices; j++ )
+ {
+ // NOTE: The UV indices are also used to index into wrinkle data
+ int nUVIndex = pUVIndices.Get( j );
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.NormalDelta3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Wrinkle1f( pVertexDelta[nUVIndex].m_flDeltaWrinkle );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End( false, false );
+ pMesh->SetFlexMesh( pFlexDelta, nFlexVertexOffset );
+ }
+
+ // build the mesh
+ int nIndices = pFaceSet->GetTriangulatedIndexCount();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertices, nIndices );
+
+ const CDmrArrayConst<Vector2D> pUVData = bHasTexCoords ? pBindBase->GetVertexData( uvField ) : NULL;
+ const CDmrArrayConst<int> pColorIndices = bHasColors ? pBindBase->GetIndexData( colorField ) : NULL;
+ const CDmrArrayConst<Color> pColorData = bHasColors ? pBindBase->GetVertexData( colorField ) : NULL;
+
+ Vector vecPosition, vecNormal;
+ Vector4D vecTangent;
+ for ( int vi = 0; vi < nVertices; ++vi )
+ {
+ renderInfo.ComputeVertex( vi, pPoseToWorld, pVertexDelta, &vecPosition, &vecNormal, &vecTangent );
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.UserData( vecTangent.Base() );
+
+ if ( pUVData.IsValid() )
+ {
+ int uvi = pUVIndices.Get( vi );
+ Vector2D uv = pUVData.Get( uvi );
+ if ( pBindBase->IsVCoordinateFlipped() )
+ {
+ uv.y = 1.0f - uv.y;
+ }
+
+ if ( bHasActiveDeltaStates )
+ {
+ uv += pVertexDelta[uvi].m_vecDeltaUV;
+ }
+ meshBuilder.TexCoord2fv( 0, uv.Base() );
+ }
+ else
+ {
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ }
+
+ if ( pColorIndices.IsValid() )
+ {
+ int ci = pColorIndices.Get( vi );
+ int color = pColorData.Get( ci ).GetRawColor();
+ meshBuilder.Color4ubv( (unsigned char*)&color );
+ }
+ else
+ {
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ }
+
+ meshBuilder.AdvanceVertex();
+ }
+
+ WriteTriangluatedIndices( pBindBase, pFaceSet, meshBuilder );
+
+ meshBuilder.End();
+
+ pMesh->Draw();
+
+ if ( pDrawSettings && pDrawSettings->GetNormals() )
+ {
+ RenderNormals( pPoseToWorld, bHasActiveDeltaStates ? pVertexDelta : NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders normals
+//-----------------------------------------------------------------------------
+#define NORMAL_LINE_SIZE 0.25f
+
+void CDmeMesh::RenderNormals( matrix3x4_t *pPoseToWorld, RenderVertexDelta_t *pDelta )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ CRenderInfo renderInfo( pBind );
+
+ Assert( renderInfo.HasPositionData() );
+ if ( !renderInfo.HasNormalData() )
+ return;
+ bool bHasTangents = renderInfo.HasTangentData();
+
+ // build the mesh
+ InitializeNormalMaterial();
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_NormalMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ int nMaxIndices, nMaxVertices;
+ pRenderContext->GetMaxToRender( pMesh, false, &nMaxVertices, &nMaxIndices );
+ int nFirstVertex = 0;
+ int nVerticesRemaining = pBind->VertexCount();;
+ int nFactor = bHasTangents ? 6 : 2;
+
+ while ( nVerticesRemaining > 0 )
+ {
+ int nVertices = nVerticesRemaining;
+ if ( nVertices > nMaxVertices / nFactor )
+ {
+ nVertices = nMaxVertices / nFactor;
+ }
+ if ( nVertices > nMaxIndices / nFactor )
+ {
+ nVertices = nMaxIndices / nFactor;
+ }
+ nVerticesRemaining -= nVertices;
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, bHasTangents ? nVertices * 3 : nVertices );
+
+ Vector vecPosition, vecNormal, vecEndPoint, vecTangentS, vecTangentT;
+ Vector4D vecTangent;
+ for ( int vi = nFirstVertex; vi < nVertices; ++vi )
+ {
+ renderInfo.ComputeVertex( vi, pPoseToWorld, pDelta, &vecPosition, &vecNormal, &vecTangent );
+
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vecPosition, NORMAL_LINE_SIZE, vecNormal, vecEndPoint );
+ meshBuilder.Position3fv( vecEndPoint.Base() );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ continue;
+
+ if ( !bHasTangents )
+ continue;
+
+ CrossProduct( vecNormal, vecTangent.AsVector3D(), vecTangentT );
+ VectorNormalize( vecTangentT );
+ // NOTE: This is the new, desired tangentS morphing behavior
+ // CrossProduct( vecTangentT, vecNormal, vecTangentS );
+ VectorCopy( vecTangent.AsVector3D(), vecTangentS );
+ vecTangentT *= vecTangent.w;
+
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vecPosition, NORMAL_LINE_SIZE, vecTangentS, vecEndPoint );
+ meshBuilder.Position3fv( vecEndPoint.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vecPosition, NORMAL_LINE_SIZE, vecTangentT, vecEndPoint );
+ meshBuilder.Position3fv( vecEndPoint.Base() );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ nFirstVertex += nVertices;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the passed DmeFaceSet in wireframe mode
+//-----------------------------------------------------------------------------
+void CDmeMesh::DrawWireframeFaceSet( CDmeFaceSet *pFaceSet, matrix3x4_t *pPoseToWorld, bool bHasActiveDeltaStates, CDmeDrawSettings *pDrawSettings )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ const FieldIndex_t posField = pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( posField < 0 )
+ return;
+
+ const CUtlVector< Vector > &posData( CDmrArrayConst< Vector >( pBind->GetVertexData( posField ) ).Get() );
+ const int nVertices = posData.Count();
+
+ const CUtlVector< int > &posIndices( CDmrArrayConst< int >( pBind->GetIndexData( posField ) ).Get() );
+
+ Vector *pDeltaVertices = bHasActiveDeltaStates ? pDeltaVertices = reinterpret_cast< Vector * >( alloca( nVertices * sizeof( Vector ) ) ) : NULL;
+
+ if ( bHasActiveDeltaStates )
+ {
+ memset( pDeltaVertices, 0, sizeof( Vector ) * nVertices );
+ const int nCount = m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ].Count();
+
+ const FieldIndex_t nBalanceFieldIndex = pBind->FindFieldIndex( CDmeVertexDeltaData::FIELD_BALANCE );
+ const FieldIndex_t nSpeedFieldIndex = pBind->FindFieldIndex( CDmeVertexDeltaData::FIELD_MORPH_SPEED );
+ const bool bDoLag = ( nSpeedFieldIndex >= 0 );
+
+ if ( nBalanceFieldIndex < 0 )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWeight = m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ][ i ].x;
+ float flLaggedWeight = m_DeltaStateWeights[ MESH_DELTA_WEIGHT_LAGGED ][ i ].x;
+ if ( flWeight <= 0.0f && ( !bDoLag || flLaggedWeight <= 0.0f ) )
+ continue;
+
+ AddVertexDelta< Vector >( pBind, pDeltaVertices, sizeof( Vector ), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].y;
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].y;
+ if ( flLeftWeight <= 0.0f && flRightWeight <= 0.0f && ( !bDoLag || ( flLeftWeightLagged <= 0.0f && flRightWeightLagged <= 0.0f ) ) )
+ continue;
+
+ AddStereoVertexDelta< Vector >( pBind, pDeltaVertices, sizeof( Vector ), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ }
+ }
+ }
+
+ Vector *pVertices = reinterpret_cast< Vector * >( alloca( nVertices * sizeof( Vector ) ) );
+
+ CRenderInfo renderInfo( pBind );
+ Assert( renderInfo.HasPositionData() );
+
+ for ( int pi = 0; pi < nVertices; ++pi )
+ {
+ renderInfo.ComputePosition( pi, pPoseToWorld, pDeltaVertices, pVertices );
+ }
+
+ InitializeNormalMaterial();
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_NormalMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ // build the mesh
+ CMeshBuilder meshBuilder;
+
+ // Draw the polygons in the face set
+ const int nFaceSetIndices = pFaceSet->NumIndices();
+ const int *pFaceSetIndices = pFaceSet->GetIndices();
+
+ int vR = 0;
+ int vG = 0;
+ int vB = 0;
+
+ if ( pDrawSettings )
+ {
+ const Color &vColor = pDrawSettings->GetColor();
+ vR = vColor.r();
+ vG = vColor.g();
+ vB = vColor.b();
+ }
+
+ int nFaceIndices;
+ for ( int i = 0; i < nFaceSetIndices; )
+ {
+ nFaceIndices = pFaceSet->GetNextPolygonVertexCount( i );
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, nFaceIndices );
+
+ for ( int j = 0; j < nFaceIndices; ++j )
+ {
+ Assert( i < nFaceSetIndices );
+
+ int vIndex0 = posIndices[ pFaceSetIndices[ i + j ] ];
+ Assert( vIndex0 < nVertices );
+ meshBuilder.Position3fv( reinterpret_cast< float * >( pVertices + vIndex0 ) );
+ meshBuilder.Color3ub( vR, vG, vB );
+ meshBuilder.AdvanceVertex();
+
+ int vIndex1 = posIndices[ pFaceSetIndices[ i + ( ( j + 1 ) % nFaceIndices ) ] ];
+ Assert( vIndex1 < nVertices );
+ meshBuilder.Position3fv( reinterpret_cast< float * >( pVertices + vIndex1) );
+ meshBuilder.Color3ub( vR, vG, vB );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+
+ i += nFaceIndices + 1;
+ }
+
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we have active delta state data?
+//-----------------------------------------------------------------------------
+bool CDmeMesh::HasActiveDeltaStates() const
+{
+ for ( int t = 0; t < MESH_DELTA_WEIGHT_TYPE_COUNT; ++t )
+ {
+ int nCount = m_DeltaStateWeights[t].Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_DeltaStateWeights[t][i].x != 0.0f || m_DeltaStateWeights[t][i].y != 0.0f )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh
+//-----------------------------------------------------------------------------
+void CDmeMesh::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ const CDmeVertexData *pBind = GetBindBaseState();
+
+ if ( !pBind || !g_pMaterialSystem || !g_pMDLCache || !g_pStudioRender )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ const bool bHasActiveDeltaStates = HasActiveDeltaStates();
+ const bool bDrawNormals = pDrawSettings ? pDrawSettings->GetNormals() : false;
+ const CDmeDrawSettings::DrawType_t drawType = pDrawSettings ? pDrawSettings->GetDrawType() : CDmeDrawSettings::DRAW_SMOOTH;
+
+ const bool bShaded = ( drawType == CDmeDrawSettings::DRAW_SMOOTH || drawType == CDmeDrawSettings::DRAW_FLAT );
+ const bool bWireframe = ( drawType == CDmeDrawSettings::DRAW_WIREFRAME );
+// const bool bBoundingBox = ( drawType == CDmeDrawSettings::DRAW_BOUNDINGBOX );
+
+ const bool bSoftwareSkinning = bHasActiveDeltaStates | bDrawNormals | bWireframe;
+
+ matrix3x4_t *pPoseToWorld = CDmeModel::SetupModelRenderState( shapeToWorld, pBind->HasSkinningData(), bSoftwareSkinning );
+
+ pRenderContext->SetNumBoneWeights( pPoseToWorld ? 0 : pBind->JointCount() );
+
+ int nFaceSets = FaceSetCount();
+ m_hwFaceSets.EnsureCount( nFaceSets );
+
+ const bool bindMaterial = pDrawSettings ? !pDrawSettings->IsAMaterialBound() : true;
+
+ for ( int fi = 0; fi < nFaceSets; ++fi )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( fi );
+
+ if ( bWireframe )
+ {
+ DrawWireframeFaceSet( pFaceSet, pPoseToWorld, bHasActiveDeltaStates, pDrawSettings );
+ continue;
+ }
+
+ if ( bindMaterial )
+ {
+ pRenderContext->Bind( pFaceSet->GetMaterial()->GetCachedMTL() );
+ }
+
+ if ( pPoseToWorld || ( bShaded && bSoftwareSkinning ) )
+ {
+ DrawDynamicMesh( pFaceSet, pPoseToWorld, bHasActiveDeltaStates, pDrawSettings );
+ continue;
+ }
+
+ // TODO: figure out how to tell the mesh when the faceset's indices change
+ if ( !m_hwFaceSets[fi].m_bBuilt )
+ {
+ m_hwFaceSets[fi].m_pMesh = CreateHwMesh( pFaceSet );
+ m_hwFaceSets[fi].m_bBuilt = true;
+ }
+
+ if ( m_hwFaceSets[fi].m_pMesh )
+ {
+ m_hwFaceSets[fi].m_pMesh->Draw();
+ }
+ }
+
+ pRenderContext->SetNumBoneWeights( 0 );
+ CDmeModel::CleanupModelRenderState();
+}
+
+
+//-----------------------------------------------------------------------------
+// Face sets
+//-----------------------------------------------------------------------------
+int CDmeMesh::FaceSetCount() const
+{
+ return m_FaceSets.Count();
+}
+
+CDmeFaceSet *CDmeMesh::GetFaceSet( int faceSetIndex )
+{
+ return m_FaceSets[ faceSetIndex ];
+}
+
+const CDmeFaceSet *CDmeMesh::GetFaceSet( int faceSetIndex ) const
+{
+ return m_FaceSets[ faceSetIndex ];
+}
+
+void CDmeMesh::AddFaceSet( CDmeFaceSet *faceSet )
+{
+ m_FaceSets.AddToTail( faceSet );
+}
+
+void CDmeMesh::RemoveFaceSet( int faceSetIndex )
+{
+ m_FaceSets.Remove( faceSetIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Find a base state by name
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::FindBaseState( const char *pStateName ) const
+{
+ const int nBaseStateCount = BaseStateCount();
+ for ( int i = 0; i < nBaseStateCount; ++i )
+ {
+ CDmeVertexData *pBaseState = GetBaseState( i );
+ if ( !Q_stricmp( pStateName, pBaseState->GetName() ) )
+ return pBaseState;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find a base state by name, add a new one if not found
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::FindOrCreateBaseState( const char *pStateName )
+{
+ CDmeVertexData *pBaseState = FindBaseState( pStateName );
+ if ( pBaseState )
+ return pBaseState;
+
+ pBaseState = CreateElement< CDmeVertexData >( pStateName, GetFileId() );
+ m_BaseStates.AddToTail( pBaseState );
+
+ return pBaseState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove a base state by name
+//-----------------------------------------------------------------------------
+bool CDmeMesh::DeleteBaseState( const char *pStateName )
+{
+ const int nBaseStateCount = BaseStateCount();
+ for ( int i = 0; i < nBaseStateCount; ++i )
+ {
+ const CDmeVertexData *pBaseState = GetBaseState( i );
+ if ( !Q_stricmp( pStateName, pBaseState->GetName() ) )
+ {
+ m_BaseStates.Remove( i );
+ g_pDataModel->DestroyElement( pBaseState->GetHandle() );
+
+ // TODO: Fix up all dependent states
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a particular base state to be current state
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetCurrentBaseState( const char *pStateName )
+{
+ m_CurrentBaseState = FindBaseState( pStateName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a particular base state to be current state
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::GetCurrentBaseState()
+{
+ return m_CurrentBaseState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a particular base state to be current state
+//-----------------------------------------------------------------------------
+const CDmeVertexData *CDmeMesh::GetCurrentBaseState() const
+{
+ return m_CurrentBaseState;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::SetBindBaseState( CDmeVertexData *pBaseState )
+{
+ if ( !pBaseState )
+ return false;
+
+ CDmeVertexData *pCheckState = FindBaseState( pBaseState->GetName() );
+ if ( pCheckState != pBaseState )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::GetBindBaseState()
+{
+ if ( m_BindBaseState.GetElement() )
+ return m_BindBaseState;
+
+ // Backwards compatibility
+ return FindBaseState( "bind" );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const CDmeVertexData *CDmeMesh::GetBindBaseState() const
+{
+ if ( m_BindBaseState.GetElement() )
+ return m_BindBaseState;
+
+ // Backwards compatibility
+ return FindBaseState( "bind" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Delta states
+//-----------------------------------------------------------------------------
+int CDmeMesh::DeltaStateCount() const
+{
+ return m_DeltaStates.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the delta
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::GetDeltaState( int nDeltaIndex ) const
+{
+ if ( nDeltaIndex < 0 || nDeltaIndex >= m_DeltaStates.Count() )
+ return NULL;
+
+ return m_DeltaStates[ nDeltaIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a delta state by name. If it isn't found, return NULL
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::FindDeltaState( const char *pDeltaName ) const
+{
+ return GetDeltaState( FindDeltaStateIndex( pDeltaName ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int SortDeltaNameFunc( const void *a, const void *b )
+{
+ return Q_strcmp( *( const char ** )( a ), *( const char ** )( b ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// If the name doe
+//-----------------------------------------------------------------------------
+const char *SortDeltaName( const char *pInDeltaName, char *pOutDeltaName, int nOutDeltaNameBufLen )
+{
+ if ( !pInDeltaName || !strchr( pInDeltaName, '_' ) )
+ return pInDeltaName;
+
+ char **ppDeltaNames = reinterpret_cast< char ** >( stackalloc( nOutDeltaNameBufLen * sizeof( char * ) ) );
+ memset( ppDeltaNames, 0, nOutDeltaNameBufLen * sizeof( char * ) );
+
+ const char *pStart = pInDeltaName;
+ int nDimensionCount = 0;
+ while ( pStart )
+ {
+ const char *pUnderBar = strchr( pStart, '_' );
+ const int nControlNameBufLen = ( pUnderBar ? pUnderBar - pStart : Q_strlen( pStart ) ) + 1;
+
+ if ( nControlNameBufLen )
+ {
+ ppDeltaNames[ nDimensionCount ] = reinterpret_cast< char * >( stackalloc( nControlNameBufLen * sizeof( char ) ) );
+ Q_strncpy( ppDeltaNames[ nDimensionCount ], pStart, nControlNameBufLen );
+ ++nDimensionCount;
+ }
+
+ pStart = pUnderBar;
+ if ( pStart )
+ {
+ ++pStart;
+ }
+ }
+
+ // This should only happen if the input name is all _'s
+ if ( nDimensionCount <= 0 )
+ return pInDeltaName;
+
+ qsort( ppDeltaNames, nDimensionCount, sizeof( char * ), SortDeltaNameFunc );
+
+ char *pDst = pOutDeltaName;
+ for ( int i = 0; i < nDimensionCount; ++i )
+ {
+ if ( i != 0 )
+ {
+ Q_strncpy( pDst, "_", nOutDeltaNameBufLen );
+ ++pDst;
+ --nOutDeltaNameBufLen;
+ }
+
+ const int nControlNameLen = Q_strlen( ppDeltaNames[ i ] );
+ Q_strncpy( pDst, ppDeltaNames[ i ], nOutDeltaNameBufLen );
+ pDst += nControlNameLen;
+ nOutDeltaNameBufLen -= nControlNameLen;
+ }
+
+ return pOutDeltaName;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::FindOrCreateDeltaState( const char *pInDeltaName )
+{
+ CDmeVertexDeltaData *pDeltaState = FindDeltaState( pInDeltaName );
+ if ( pDeltaState )
+ return pDeltaState;
+
+ const int nDeltaNameBufLen = Q_strlen( pInDeltaName ) + 1;
+ char *pDeltaNameBuf = reinterpret_cast< char * >( stackalloc( nDeltaNameBufLen * sizeof( char ) ) );
+ const char *pDeltaName = SortDeltaName( pInDeltaName, pDeltaNameBuf, nDeltaNameBufLen );
+
+ pDeltaState = CreateElement< CDmeVertexDeltaData >( pDeltaName, GetFileId() );
+ if ( pDeltaState )
+ {
+ m_DeltaStates.AddToTail( pDeltaState );
+ }
+
+ return pDeltaState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a delta state index by comparing names, if it can't be found
+// searches for all permutations of the delta name
+//-----------------------------------------------------------------------------
+int CDmeMesh::FindDeltaStateIndex( const char *pInDeltaName ) const
+{
+ const char *pDeltaName = pInDeltaName;
+
+ if ( strchr( pInDeltaName, '_' ) )
+ {
+ const int nDeltaNameBufLen = Q_strlen( pInDeltaName ) + 1;
+ char *pDeltaNameBuf = reinterpret_cast< char * >( stackalloc( nDeltaNameBufLen * sizeof( char ) ) );
+ pDeltaName = SortDeltaName( pInDeltaName, pDeltaNameBuf, nDeltaNameBufLen );
+ }
+
+ int dn = DeltaStateCount();
+ for ( int di = 0; di < dn; ++di )
+ {
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( di );
+ if ( !Q_stricmp( pDeltaName, pDeltaState->GetName() ) )
+ return di;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaStateWeight( int nDeltaIndex, MeshDeltaWeightType_t type, float flMorphWeight )
+{
+ if ( nDeltaIndex < m_DeltaStateWeights[type].Count() )
+ {
+ m_DeltaStateWeights[type].Set( nDeltaIndex, Vector2D( flMorphWeight, flMorphWeight ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaStateWeight( int nDeltaIndex, MeshDeltaWeightType_t type, float flLeftWeight, float flRightWeight )
+{
+ if ( nDeltaIndex < m_DeltaStateWeights[type].Count() )
+ {
+ m_DeltaStateWeights[type].Set( nDeltaIndex, Vector2D( flLeftWeight, flRightWeight ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Determines the appropriate vertex format for hardware meshes
+//-----------------------------------------------------------------------------
+VertexFormat_t CDmeMesh::ComputeHwMeshVertexFormat( void )
+{
+ bool bIsDX7 = !g_pMaterialSystemHardwareConfig->SupportsVertexAndPixelShaders();
+ VertexFormat_t vertexFormat = VERTEX_POSITION | VERTEX_COLOR | VERTEX_NORMAL | VERTEX_TEXCOORD_SIZE(0,2) | VERTEX_BONEWEIGHT(2) | VERTEX_BONE_INDEX
+ | ( bIsDX7 ? 0 : VERTEX_USERDATA_SIZE(4) );
+
+ // FIXME: set VERTEX_FORMAT_COMPRESSED if there are no artifacts and if it saves enough memory (use 'mem_dumpvballocs')
+ // vertexFormat |= VERTEX_FORMAT_COMPRESSED;
+ // FIXME: check for and strip unused vertex elements (see 'bHasNormals', etc, in CreateHwMesh below)
+
+ return vertexFormat;
+}
+
+//-----------------------------------------------------------------------------
+// Builds a hardware mesh
+//-----------------------------------------------------------------------------
+IMesh *CDmeMesh::CreateHwMesh( CDmeFaceSet *pFaceSet )
+{
+ const CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return NULL;
+
+ // NOTE: This is memory inefficient. We create a copy of all vertices
+ // for each face set, even if those vertices aren't used by the face set
+ // Mostly chose to do this for code simplicity, although it also is faster to generate meshes
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ VertexFormat_t vertexFormat = ComputeHwMeshVertexFormat( );
+ IMesh *pMesh = pRenderContext->CreateStaticMesh( vertexFormat, "dmemesh" );
+
+ CMeshBuilder meshBuilder;
+
+ // prepare vertices
+ FieldIndex_t posField = pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t normalField = pBind->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ FieldIndex_t tangentField = pBind->FindFieldIndex( CDmeVertexData::FIELD_TANGENT );
+ FieldIndex_t uvField = pBind->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ FieldIndex_t colorField = pBind->FindFieldIndex( CDmeVertexData::FIELD_COLOR );
+
+ Assert( posField >= 0 );
+ bool bHasNormals = ( normalField >= 0 );
+ bool bHasTangent = ( tangentField >= 0 );
+ bool bHasTexCoords = ( uvField >= 0 );
+ bool bHasColors = ( colorField >= 0 );
+
+ // build the mesh
+ int nIndices = pFaceSet->GetTriangulatedIndexCount();
+ int nVertices = pBind->VertexCount();
+ int nJointCount = pBind->JointCount();
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertices, nIndices );
+
+ const CDmrArrayConst<int> pPositionIndices = pBind->GetIndexData( posField );
+ const CDmrArrayConst<Vector> pPositionData = pBind->GetVertexData( posField );
+ const CDmrArrayConst<int> pNormalIndices = bHasNormals ? pBind->GetIndexData( normalField ) : NULL;
+ const CDmrArrayConst<Vector> pNormalData = bHasNormals ? pBind->GetVertexData( normalField ) : NULL;
+ const CDmrArrayConst<int> pTangentIndices = bHasTangent ? pBind->GetIndexData( tangentField ) : NULL;
+ const CDmrArrayConst<Vector4D> pTangentData = bHasTangent ? pBind->GetVertexData( tangentField ) : NULL;
+ const CDmrArrayConst<int> pUVIndices = bHasTexCoords ? pBind->GetIndexData( uvField ) : NULL;
+ const CDmrArrayConst<Vector2D> pUVData = bHasTexCoords ? pBind->GetVertexData( uvField ) : NULL;
+ const CDmrArrayConst<int> pColorIndices = bHasColors ? pBind->GetIndexData( colorField ) : NULL;
+ const CDmrArrayConst<Color> pColorData = bHasColors ? pBind->GetVertexData( colorField ) : NULL;
+
+ Vector4D defaultTangentS( 1.0f, 0.0f, 0.0f, 1.0f );
+ for ( int vi = 0; vi < nVertices; ++vi )
+ {
+ meshBuilder.Position3fv( pPositionData.Get( pPositionIndices.Get( vi ) ).Base() );
+ if ( pNormalData.IsValid() )
+ {
+ meshBuilder.Normal3fv( pNormalData.Get( pNormalIndices.Get( vi ) ).Base() );
+ }
+ else
+ {
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ }
+ if ( pTangentData.IsValid() )
+ {
+ meshBuilder.UserData( pTangentData.Get( pTangentIndices.Get( vi ) ).Base() );
+ }
+ else
+ {
+ meshBuilder.UserData( defaultTangentS.Base() );
+ }
+ if ( pUVData.IsValid() )
+ {
+ const Vector2D &uv = pUVData.Get( pUVIndices.Get( vi ) );
+ if ( !pBind->IsVCoordinateFlipped() )
+ {
+ meshBuilder.TexCoord2fv( 0, uv.Base() );
+ }
+ else
+ {
+ meshBuilder.TexCoord2f( 0, uv.x, 1.0f - uv.y );
+ }
+ }
+ else
+ {
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ }
+ if ( pColorIndices.IsValid() )
+ {
+ int color = pColorData.Get( pColorIndices.Get( vi ) ).GetRawColor();
+ meshBuilder.Color4ubv( (unsigned char*)&color );
+ }
+ else
+ {
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ }
+
+ // FIXME: Note that this will break once we exceeed the max joint count
+ // that the hardware can handle
+ const float *pJointWeight = pBind->GetJointWeights( vi );
+ const int *pJointIndices = pBind->GetJointIndices( vi );
+ for ( int i = 0; i < nJointCount; ++i )
+ {
+ meshBuilder.BoneWeight( i, pJointWeight[i] );
+ meshBuilder.BoneMatrix( i, pJointIndices[i] );
+ }
+
+ for ( int i = nJointCount; i < 4; ++i )
+ {
+ meshBuilder.BoneWeight( i, ( i == 0 ) ? 1.0f : 0.0f );
+ meshBuilder.BoneMatrix( i, 0 );
+ }
+
+ meshBuilder.AdvanceVertex();
+ }
+
+ // prepare indices
+ int nFirstIndex = 0;
+ int nIndexCount = pFaceSet->NumIndices();
+ while ( nFirstIndex < nIndexCount )
+ {
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+ if ( nVertexCount >= 3 )
+ {
+ int nOutCount = ( nVertexCount-2 ) * 3;
+ int *pIndices = (int*)_alloca( nOutCount * sizeof(int) );
+ ComputeTriangulatedIndices( pBind, pFaceSet, nFirstIndex, pIndices, nOutCount );
+ for ( int ii = 0; ii < nOutCount; ++ii )
+ {
+ meshBuilder.FastIndex( pIndices[ii] );
+ }
+ }
+ nFirstIndex += nVertexCount + 1;
+ }
+
+ meshBuilder.End();
+
+ return pMesh;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute triangulated indices
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeTriangulatedIndices( const CDmeVertexData *pBaseState, CDmeFaceSet *pFaceSet, int nFirstIndex, int *pIndices, int nOutCount )
+{
+ // FIXME: Come up with a more efficient way of computing this
+ // This involves a bunch of recomputation of distances
+ float flMinDistance = FLT_MAX;
+ int nMinIndex = 0;
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+
+ // Optimization for quads + triangles.. it's totally symmetric
+ int nLoopCount = nVertexCount;
+ if ( nVertexCount <= 3 )
+ {
+ nLoopCount = 0;
+ }
+ else if ( nVertexCount == 4 )
+ {
+ nLoopCount = 2;
+ }
+
+ for ( int i = 0; i < nLoopCount; ++i )
+ {
+ float flDistance = 0.0f;
+ const Vector &vecCenter = pBaseState->GetPosition( pFaceSet->GetIndex( nFirstIndex+i ) );
+ for ( int j = 2; j < nVertexCount-1; ++j )
+ {
+ int vi = ( i + j ) % nVertexCount;
+ const Vector &vecEdge = pBaseState->GetPosition( pFaceSet->GetIndex( nFirstIndex+vi ) );
+ flDistance += vecEdge.DistTo( vecCenter );
+ }
+
+ if ( flDistance < flMinDistance )
+ {
+ nMinIndex = i;
+ flMinDistance = flDistance;
+ }
+ }
+
+ // Compute the triangulation indices
+ Assert( nOutCount == ( nVertexCount - 2 ) * 3 );
+ int nOutIndex = 0;
+ for ( int i = 1; i < nVertexCount - 1; ++i )
+ {
+ pIndices[nOutIndex++] = pFaceSet->GetIndex( nFirstIndex + nMinIndex );
+ pIndices[nOutIndex++] = pFaceSet->GetIndex( nFirstIndex + ((nMinIndex + i) % nVertexCount) );
+ pIndices[nOutIndex++] = pFaceSet->GetIndex( nFirstIndex + ((nMinIndex + i + 1) % nVertexCount) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Build a map from vertex index to a list of triangles that share the vert.
+//-----------------------------------------------------------------------------
+void CDmeMesh::BuildTriangleMap( const CDmeVertexData *pBaseState, CDmeFaceSet* pFaceSet, CUtlVector<Triangle_t>& triangles, CUtlVector< CUtlVector<int> >* pVertToTriMap )
+{
+ // prepare indices
+ int nFirstIndex = 0;
+ int nIndexCount = pFaceSet->NumIndices();
+ while ( nFirstIndex < nIndexCount )
+ {
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+ if ( nVertexCount >= 3 )
+ {
+ int nOutCount = ( nVertexCount-2 ) * 3;
+ int *pIndices = (int*)_alloca( nOutCount * sizeof(int) );
+ ComputeTriangulatedIndices( pBaseState, pFaceSet, nFirstIndex, pIndices, nOutCount );
+ for ( int ii = 0; ii < nOutCount; ii += 3 )
+ {
+ int t = triangles.AddToTail();
+ Triangle_t& triangle = triangles[t];
+
+ triangle.m_nIndex[0] = pIndices[ii];
+ triangle.m_nIndex[1] = pIndices[ii+1];
+ triangle.m_nIndex[2] = pIndices[ii+2];
+
+ if ( pVertToTriMap )
+ {
+ (*pVertToTriMap)[ pIndices[ii] ].AddToTail( t );
+ (*pVertToTriMap)[ pIndices[ii+1] ].AddToTail( t );
+ (*pVertToTriMap)[ pIndices[ii+2] ].AddToTail( t );
+ }
+ }
+ }
+ nFirstIndex += nVertexCount + 1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes tangent space data for triangles
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeTriangleTangets( const CDmeVertexData *pVertexData, CUtlVector<Triangle_t>& triangles )
+{
+ // Calculate the tangent space for each triangle.
+ int nTriangleCount = triangles.Count();
+ for ( int triID = 0; triID < nTriangleCount; triID++ )
+ {
+ Triangle_t &triangle = triangles[triID];
+
+ const Vector &p0 = pVertexData->GetPosition( triangle.m_nIndex[0] );
+ const Vector &p1 = pVertexData->GetPosition( triangle.m_nIndex[1] );
+ const Vector &p2 = pVertexData->GetPosition( triangle.m_nIndex[2] );
+ const Vector2D &t0 = pVertexData->GetTexCoord( triangle.m_nIndex[0] );
+ const Vector2D &t1 = pVertexData->GetTexCoord( triangle.m_nIndex[1] );
+ const Vector2D &t2 = pVertexData->GetTexCoord( triangle.m_nIndex[2] );
+ CalcTriangleTangentSpace( p0, p1, p2, t0, t1, t2, triangle.m_vecTangentS, triangle.m_vecTangentT );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Build a map from vertex index to a list of triangles that share the vert.
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeAverageTangent( CDmeVertexData *pVertexData, bool bSmoothTangents, CUtlVector< CUtlVector<int> >& vertToTriMap, CUtlVector<Triangle_t>& triangles )
+{
+ FieldIndex_t posField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t normalField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ FieldIndex_t tangentField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_TANGENT );
+
+ const CDmrArray<int> pPositionIndices = pVertexData->GetIndexData( posField );
+ const CDmrArray<Vector> pPositionData = pVertexData->GetVertexData( posField );
+ const CDmrArray<int> pNormalIndices = pVertexData->GetIndexData( normalField );
+ const CDmrArray<Vector> pNormalData = pVertexData->GetVertexData( normalField );
+
+ // calculate an average tangent space for each vertex.
+ int nVertexCount = pVertexData->VertexCount();
+ Vector4D finalSVect;
+ for( int vertID = 0; vertID < nVertexCount; vertID++ )
+ {
+ CUtlVector<int> &triangleList = vertToTriMap[vertID];
+
+ Vector sVect, tVect;
+ sVect.Init( 0.0f, 0.0f, 0.0f );
+ tVect.Init( 0.0f, 0.0f, 0.0f );
+ int nTriangleCount = triangleList.Count();
+ for ( int triID = 0; triID < nTriangleCount; triID++ )
+ {
+ Triangle_t &tri = triangles[ triangleList[triID] ];
+ sVect += tri.m_vecTangentS;
+ tVect += tri.m_vecTangentT;
+ }
+
+ // In the case of zbrush, everything needs to be treated as smooth.
+ if ( bSmoothTangents )
+ {
+ const Vector &vertPos1 = pPositionData.Get( pPositionIndices.Get( vertID ) );
+ for( int vertID2 = 0; vertID2 < nVertexCount; vertID2++ )
+ {
+ if ( vertID2 == vertID )
+ continue;
+
+ const Vector &vertPos2 = pPositionData.Get( pPositionIndices.Get( vertID2 ) );
+ if ( vertPos1 != vertPos2 )
+ continue;
+
+ CUtlVector<int> &triangleList2 = vertToTriMap[vertID2];
+ int nTriangleCount2 = triangleList2.Count();
+ for ( int triID2 = 0; triID2 < nTriangleCount2; triID2++ )
+ {
+ Triangle_t &tri2 = triangles[ triangleList2[triID2] ];
+ sVect += tri2.m_vecTangentS;
+ tVect += tri2.m_vecTangentT;
+ }
+ }
+ }
+
+ // make an orthonormal system.
+ // need to check if we are left or right handed.
+ Vector tmpVect;
+ CrossProduct( sVect, tVect, tmpVect );
+ const Vector &normal = pNormalData.Get( pNormalIndices.Get( vertID ) );
+ bool bLeftHanded = DotProduct( tmpVect, normal ) < 0.0f;
+ if ( !bLeftHanded )
+ {
+ CrossProduct( normal, sVect, tVect );
+ CrossProduct( tVect, normal, sVect );
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+ finalSVect[0] = sVect[0];
+ finalSVect[1] = sVect[1];
+ finalSVect[2] = sVect[2];
+ finalSVect[3] = 1.0f;
+ }
+ else
+ {
+ CrossProduct( sVect, normal, tVect );
+ CrossProduct( normal, tVect, sVect );
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+ finalSVect[0] = sVect[0];
+ finalSVect[1] = sVect[1];
+ finalSVect[2] = sVect[2];
+ finalSVect[3] = -1.0f;
+ }
+
+ pVertexData->SetVertexData( tangentField, vertID, 1, AT_VECTOR4, &finalSVect );
+ pVertexData->SetVertexIndices( tangentField, vertID, 1, &vertID );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a map from vertex index to all triangles that use it
+//-----------------------------------------------------------------------------
+void CDmeMesh::BuildVertToTriMap( const CDmeVertexData *pVertexData, CUtlVector<Triangle_t> &triangles, CUtlVector< CUtlVector<int> > &vertToTriMap )
+{
+ vertToTriMap.AddMultipleToTail( pVertexData->VertexCount() );
+
+ int nCount = FaceSetCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( i );
+ BuildTriangleMap( pVertexData, pFaceSet, triangles, &vertToTriMap );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute a default per-vertex tangent given normal data + uv data
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDefaultTangentData( CDmeVertexData *pVertexData, bool bSmoothTangents )
+{
+ if ( !pVertexData )
+ return;
+
+ // Need to have valid pos, uv, and normal to perform this operation
+ FieldIndex_t posField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t normalField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ FieldIndex_t uvField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ if ( posField < 0 || uvField < 0 || normalField < 0 )
+ return;
+
+ // FIXME: Need to do a pass to make sure no vertex is referenced by
+ // multiple facesets that have different materials in them.
+ // In that case, we need to add extra copies of that vertex and modify
+ // the face set data to refer to the new vertices
+
+ // Build a map from vertex to a list of triangles that share the vert.
+ CUtlVector<Triangle_t> triangles( 0, 1024 );
+ CUtlVector< CUtlVector<int> > vertToTriMap;
+ vertToTriMap.AddMultipleToTail( pVertexData->VertexCount() );
+
+ int nCount = FaceSetCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( i );
+ BuildTriangleMap( pVertexData, pFaceSet, triangles, &vertToTriMap );
+ }
+
+ ComputeTriangleTangets( pVertexData, triangles );
+
+ // FIXME: We could do a pass to determine the unique combinations of
+ // position + tangent indices in the vertex data. We only need to have
+ // a unique tangent for each of these unique vertices. For simplicity
+ // (and speed), I'll assume all tangents are unique per vertex.
+ FieldIndex_t tangent = pVertexData->CreateField<Vector4D>( "tangents" );
+ pVertexData->RemoveAllVertexData( tangent );
+ pVertexData->AddVertexData( tangent, pVertexData->VertexCount() );
+
+ ComputeAverageTangent( pVertexData, bSmoothTangents, vertToTriMap, triangles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute a default per-vertex tangent given normal data + uv data for all vertex data referenced by this mesh
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDefaultTangentData( bool bSmoothTangents )
+{
+ const int nBaseStateCount = m_BaseStates.Count();
+ for ( int i = 0; i < nBaseStateCount; ++i )
+ {
+ if ( m_BaseStates[i] && m_BaseStates[i]->NeedsTangentData() )
+ {
+ ComputeDefaultTangentData( m_BaseStates[i], bSmoothTangents );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Utility method to compute default tangent data on all meshes in the sub-dag hierarchy
+//-----------------------------------------------------------------------------
+void ComputeDefaultTangentData( CDmeDag *pDag, bool bSmoothTangents )
+{
+ if ( !pDag )
+ return;
+
+ CDmeMesh *pMesh = CastElement< CDmeMesh >( pDag->GetShape() );
+ if ( pMesh )
+ {
+ pMesh->ComputeDefaultTangentData( bSmoothTangents );
+ }
+
+ int nChildCount = pDag->GetChildCount();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ ComputeDefaultTangentData( pDag->GetChild( i ), bSmoothTangents );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the dimensionality of the delta state (how many inputs affect it)
+//-----------------------------------------------------------------------------
+int CDmeMesh::ComputeDeltaStateDimensionality( int nDeltaIndex )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nDeltaIndex );
+ const char *pDeltaStateName = pDeltaState->GetName();
+
+ const char *pUnderBar = pDeltaStateName;
+ int nDimensions = 0;
+ while ( pUnderBar )
+ {
+ ++nDimensions;
+ pUnderBar = strchr( pUnderBar, '_' );
+ if ( pUnderBar )
+ {
+ ++pUnderBar;
+ }
+ }
+
+ return nDimensions;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the aggregate position for all vertices after applying a set of delta states
+//-----------------------------------------------------------------------------
+void CDmeMesh::AddDelta( CDmeVertexData *pBaseState, Vector *pDeltaPosition, int nDeltaStateIndex, CDmeVertexData::StandardFields_t fieldId )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nDeltaStateIndex );
+ FieldIndex_t nFieldIndex = pDeltaState->FindFieldIndex( fieldId );
+ if ( nFieldIndex < 0 )
+ return;
+
+ if ( pBaseState->FindFieldIndex( CDmeVertexData::FIELD_BALANCE ) != -1 )
+ {
+ AddStereoVertexDelta<Vector>( pBaseState, pDeltaPosition, sizeof(Vector), fieldId, nDeltaStateIndex, true );
+ }
+ else
+ {
+ AddVertexDelta<Vector>( pBaseState, pDeltaPosition, sizeof(Vector), fieldId, nDeltaStateIndex, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes correctly averaged vertex normals from position data
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeNormalsFromPositions(
+ CDmeVertexData *pBase,
+ const Vector *pPosition,
+ const CUtlVector<Triangle_t> &triangles,
+ int nNormalCount,
+ Vector *pNormals )
+{
+ Assert( nNormalCount == pBase->GetNormalData().Count() );
+ int *pNormalsAdded = (int*)_alloca( nNormalCount * sizeof(int) );
+ memset( pNormalsAdded, 0, nNormalCount * sizeof(int) );
+ memset( pNormals, 0, nNormalCount * sizeof(Vector) );
+
+ const CUtlVector<int> &positionIndices = pBase->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
+ const CUtlVector<int> &normalIndices = pBase->GetVertexIndexData( CDmeVertexData::FIELD_NORMAL );
+
+ int nTriangleCount = triangles.Count();
+ for ( int i = 0; i < nTriangleCount; ++i )
+ {
+ const Triangle_t &tri = triangles[i];
+ int p1 = positionIndices[ tri.m_nIndex[0] ];
+ int p2 = positionIndices[ tri.m_nIndex[1] ];
+ int p3 = positionIndices[ tri.m_nIndex[2] ];
+
+ int n1 = normalIndices[ tri.m_nIndex[0] ];
+ int n2 = normalIndices[ tri.m_nIndex[1] ];
+ int n3 = normalIndices[ tri.m_nIndex[2] ];
+
+ Vector vecDelta, vecDelta2, vecNormal;
+ VectorSubtract( pPosition[p2], pPosition[p1], vecDelta );
+ VectorSubtract( pPosition[p3], pPosition[p1], vecDelta2 );
+ CrossProduct( vecDelta, vecDelta2, vecNormal );
+ VectorNormalize( vecNormal );
+
+ pNormals[n1] += vecNormal;
+ pNormals[n2] += vecNormal;
+ pNormals[n3] += vecNormal;
+
+ ++pNormalsAdded[n1]; ++pNormalsAdded[n2]; ++pNormalsAdded[n3];
+ }
+
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ if ( pNormalsAdded[i] > 0 )
+ {
+ pNormals[i] /= pNormalsAdded[i];
+ VectorNormalize( pNormals[i] );
+ }
+ else
+ {
+ pNormals[i].Init( 0, 1, 0 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Converts pose-space normals into deltas appropriate for correction delta states
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeCorrectedNormalsFromActualNormals( const CUtlVector<int> &deltaStateList, int nNormalCount, Vector *pNormals )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ Assert( nNormalCount == pBind->GetNormalData().Count() );
+
+ // Subtract out all other normal contributions
+ Vector *pUncorrectedNormals = (Vector*)_alloca( nNormalCount * sizeof(Vector) );
+ memcpy( pUncorrectedNormals, pBind->GetNormalData().Base(), nNormalCount * sizeof( Vector ) );
+ int nDeltaStateCount = deltaStateList.Count();
+ for ( int i = 0; i < nDeltaStateCount; ++i )
+ {
+ AddDelta( pBind, pUncorrectedNormals, deltaStateList[i], CDmeVertexData::FIELD_NORMAL );
+ }
+
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ pNormals[i] -= pUncorrectedNormals[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies the corrected normal data into a delta state
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaNormalData( int nDeltaIndex, int nNormalCount, Vector *pNormals )
+{
+ // pNormals represents the correct normal delta state for this combination
+ // Copy it into the delta state for this combination.
+ // Use tolerance to deal with precision errors introduced by the various computations
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nDeltaIndex );
+ FieldIndex_t nNormalField = pDeltaState->FindFieldIndex( CDmeVertexDeltaData::FIELD_NORMAL );
+ if ( nNormalField >= 0 )
+ {
+ pDeltaState->RemoveAllVertexData( nNormalField );
+ }
+ else
+ {
+ nNormalField = pDeltaState->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ }
+
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ if ( pNormals[i].LengthSqr() < 1e-4 )
+ continue;
+
+ int nNormalIndex = pDeltaState->AddVertexData( nNormalField, 1 );
+ pDeltaState->SetVertexData( nNormalField, nNormalIndex, 1, AT_VECTOR3, &pNormals[i] );
+ pDeltaState->SetVertexIndices( nNormalField, nNormalIndex, 1, &i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Discovers the atomic controls used by the various delta states
+//-----------------------------------------------------------------------------
+static int DeltaStateUsageLessFunc( const int * lhs, const int * rhs )
+{
+ return *lhs - *rhs;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::BuildAtomicControlLists( int nCount, DeltaComputation_t *pInfo, CUtlVector< CUtlVector< int > > &deltaStateUsage )
+{
+ CUtlVector< CUtlString > atomicControls;
+ deltaStateUsage.SetCount( nCount );
+
+ // Build a list of atomic controls
+ int nCurrentDelta;
+ for ( nCurrentDelta = 0; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ if ( pInfo[nCurrentDelta].m_nDimensionality != 1 )
+ break;
+ int j = atomicControls.AddToTail( GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex )->GetName() );
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+
+ for ( ; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex );
+ int nLen = Q_strlen( pDeltaState->GetName() );
+ char *pTempBuf = (char*)_alloca( nLen + 1 );
+ memcpy( pTempBuf, pDeltaState->GetName(), nLen+1 );
+ char *pNext;
+ for ( char *pUnderBar = pTempBuf; pUnderBar; pUnderBar = pNext )
+ {
+ pNext = strchr( pUnderBar, '_' );
+ if ( pNext )
+ {
+ *pNext = 0;
+ ++pNext;
+ }
+
+ // Find this name in the list of strings
+ int j;
+ int nControlCount = atomicControls.Count();
+ for ( j = 0; j < nControlCount; ++j )
+ {
+ if ( !Q_stricmp( pUnderBar, atomicControls[j] ) )
+ break;
+ }
+ if ( j == nControlCount )
+ {
+ j = atomicControls.AddToTail( pUnderBar );
+ }
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+ deltaStateUsage[ nCurrentDelta ].Sort( DeltaStateUsageLessFunc );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Construct list of all n-1 -> 1 dimensional delta states
+// that will be active when this delta state is active
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDependentDeltaStateList( CUtlVector< DeltaComputation_t > &compList )
+{
+ if ( compList.Count() == 0 )
+ {
+ ComputeDeltaStateComputationList( compList );
+ }
+
+ CUtlVector< CUtlVector< int > > deltaStateUsage;
+ const int nCount( compList.Count() );
+ BuildAtomicControlLists( nCount, compList.Base(), deltaStateUsage );
+
+ // Now build up a list of dependent delta states based on usage
+ // NOTE: Usage is sorted in ascending order.
+ for ( int i = 1; i < nCount; ++i )
+ {
+ int nUsageCount1 = deltaStateUsage[i].Count();
+ for ( int j = 0; j < i; ++j )
+ {
+ // At the point they have the same dimensionality, no more need to check
+ if ( compList[j].m_nDimensionality == compList[i].m_nDimensionality )
+ break;
+
+ int ii = 0;
+ bool bSubsetFound = true;
+ int nUsageCount2 = deltaStateUsage[j].Count();
+ for ( int ji = 0; ji < nUsageCount2; ++ji )
+ {
+ for ( bSubsetFound = false; ii < nUsageCount1; ++ii )
+ {
+ if ( deltaStateUsage[j][ji] == deltaStateUsage[i][ii] )
+ {
+ ++ii;
+ bSubsetFound = true;
+ break;
+ }
+
+ if ( deltaStateUsage[j][ji] < deltaStateUsage[i][ii] )
+ break;
+ }
+
+ if ( !bSubsetFound )
+ break;
+ }
+
+ if ( bSubsetFound )
+ {
+ compList[i].m_DependentDeltas.AddToTail( compList[j].m_nDeltaIndex );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sorts DeltaComputation_t's by dimensionality
+//-----------------------------------------------------------------------------
+int CDmeMesh::DeltaStateLessFunc( const void * lhs, const void * rhs )
+{
+ DeltaComputation_t &info1 = *(DeltaComputation_t*)lhs;
+ DeltaComputation_t &info2 = *(DeltaComputation_t*)rhs;
+ return info1.m_nDimensionality - info2.m_nDimensionality;
+}
+
+
+//-----------------------------------------------------------------------------
+// Generates a sorted list in order of dimensionality of the delta states
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDeltaStateComputationList( CUtlVector< DeltaComputation_t > &compList )
+{
+ // Do all combinations in order of dimensionality, lowest dimension first
+ const int nCount = DeltaStateCount();
+ compList.EnsureCount( nCount ); // Resets the CUtlVector
+ for ( int i = 0; i < nCount; ++i )
+ {
+ compList[i].m_nDeltaIndex = i;
+ compList[i].m_nDimensionality = ComputeDeltaStateDimensionality( i );
+ }
+ qsort( compList.Base(), nCount, sizeof(DeltaComputation_t), DeltaStateLessFunc );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes normal deltas for all delta states based on position deltas
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDeltaStateNormals()
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ const FieldIndex_t nBindNormalIndex = pBind->CreateField( CDmeVertexData::FIELD_NORMAL );
+
+ const CUtlVector< Vector > &basePosData = pBind->GetPositionData();
+ const int nPosCount = basePosData.Count();
+
+ // Build a map from vertex to a list of triangles that share the vert.
+ CUtlVector< Triangle_t > triangles( 0, 1024 );
+ CUtlVector< CUtlVector<int> > vertToTriMap;
+ vertToTriMap.AddMultipleToTail( pBind->VertexCount() );
+
+ const int nFaceSetCount = FaceSetCount();
+ for ( int i = 0; i < nFaceSetCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( i );
+ BuildTriangleMap( pBind, pFaceSet, triangles, &vertToTriMap );
+ }
+
+ // Temporary storage for normals
+ Vector *pNormals = reinterpret_cast< Vector * >( alloca( nPosCount * sizeof( Vector ) ) );
+
+ // Make all of the normals in the bind pose smooth
+ {
+ const CUtlVector< int > &basePosIndices = pBind->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
+
+ pBind->SetVertexIndices( nBindNormalIndex, 0, basePosIndices.Count(), basePosIndices.Base() );
+ pBind->RemoveAllVertexData( nBindNormalIndex );
+ pBind->AddVertexData( nBindNormalIndex, nPosCount );
+
+ ComputeNormalsFromPositions( pBind, basePosData.Base(), triangles, nPosCount, pNormals );
+ pBind->SetVertexData( nBindNormalIndex, 0, nPosCount, AT_VECTOR3, pNormals );
+
+ // Fix up the current state to have smooth normals if current is not bind
+ CDmeVertexData *pCurrent = GetCurrentBaseState();
+ if ( pCurrent != pBind )
+ {
+ const FieldIndex_t nCurrentNormalIndex = pCurrent->CreateField( CDmeVertexData::FIELD_NORMAL );
+ pCurrent->SetVertexIndices( nCurrentNormalIndex, 0, basePosIndices.Count(), basePosIndices.Base() );
+ pCurrent->RemoveAllVertexData( nCurrentNormalIndex );
+ pCurrent->AddVertexData( nCurrentNormalIndex, nPosCount );
+
+ const CUtlVector< Vector > &currPosData = pCurrent->GetPositionData();
+ ComputeNormalsFromPositions( pCurrent, currPosData.Base(), triangles, nPosCount, pNormals );
+ pCurrent->SetVertexData( nCurrentNormalIndex, 0, nPosCount, AT_VECTOR3, pNormals );
+ }
+ }
+
+ // Temporary storage for the positions
+ Vector *pPosData = reinterpret_cast< Vector * >( alloca( nPosCount * sizeof( Vector ) ) );
+
+ // Compute the dependent delta state list like thing
+ CUtlVector< DeltaComputation_t > computationOrder;
+ ComputeDependentDeltaStateList( computationOrder );
+
+ const int nDeltaStateCount = computationOrder.Count();
+ for ( int i = 0; i < nDeltaStateCount; ++i )
+ {
+ const DeltaComputation_t &deltaComputation = computationOrder[ i ];
+
+ memcpy( pPosData, basePosData.Base(), nPosCount * sizeof( Vector ) );
+
+ const CUtlVector< int > &depDeltas = deltaComputation.m_DependentDeltas;
+ const int nDepStateCount = depDeltas.Count();
+ for ( int j = 0; j < nDepStateCount; ++j )
+ {
+ AddDelta( GetDeltaState( depDeltas[ j ] ), pPosData, nPosCount, CDmeVertexData::FIELD_POSITION );
+ }
+
+ AddDelta( GetDeltaState( deltaComputation.m_nDeltaIndex ), pPosData, nPosCount, CDmeVertexData::FIELD_POSITION );
+
+ ComputeNormalsFromPositions( pBind, pPosData, triangles, nPosCount, pNormals );
+
+ SetDeltaNormalDataFromActualNormals( computationOrder[ i ].m_nDeltaIndex, depDeltas, nPosCount, pNormals );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes normal deltas for all delta states based on position deltas
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaNormalDataFromActualNormals( int nDeltaIndex, const CUtlVector<int> &deltaStateList, int nNormalCount, Vector *pNormals )
+{
+ // Store off the current state values
+ CUtlVector< Vector2D > deltaStateWeights[MESH_DELTA_WEIGHT_TYPE_COUNT];
+ for ( int i = 0; i < MESH_DELTA_WEIGHT_TYPE_COUNT; ++i )
+ {
+ deltaStateWeights[i] = m_DeltaStateWeights[i].Get();
+
+ // Turn on the current weights to all be 1 to get max effect of morphs
+ int nCount = m_DeltaStateWeights[i].Count();
+ for ( int j = 0; j < nCount; ++j )
+ {
+ m_DeltaStateWeights[i].Set( j, Vector2D( 1.0f, 1.0f ) );
+ }
+ }
+
+ ComputeCorrectedNormalsFromActualNormals( deltaStateList, nNormalCount, pNormals );
+
+ // Finally, store the corrected normals into the delta state
+ SetDeltaNormalData( nDeltaIndex, nNormalCount, pNormals );
+
+ // Restore weights to their current value
+ for ( int i = 0; i < MESH_DELTA_WEIGHT_TYPE_COUNT; ++i )
+ {
+ m_DeltaStateWeights[i] = deltaStateWeights[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// A recursive algorithm to compute nCk, i.e. the number of order independent
+// Combinations without any repeats of k items taking n at a time
+// The size of the returned array is:
+//
+// n!
+// -------------
+// k! ( n - r )!
+//
+// e.g. 4C4 = { 0 1 2 3 }
+// e.g. 3C4 = { 0 1 2 }, { 0 1 3 }, { 0 2 3 }, { 1 2 3 }
+// e.g. 2C4 = { 0 1 }, { 0 2 }, { 0 3 }, { 1 2 }, { 1 3 }, { 2 3 }
+// e.g. 1C4 = { 0 }, { 1 }, { 2 }, { 3 }
+//
+// It's recursive and meant to be called by the user with just n, k and combos
+// the other default arguments are for the recursive steps
+//-----------------------------------------------------------------------------
+void CDmeMesh::Combinations(
+ int n,
+ int k,
+ CUtlVector< CUtlVector< int > > &combos,
+ int *pTmpArray,
+ int start,
+ int currentK )
+{
+ if ( !pTmpArray )
+ {
+ pTmpArray = reinterpret_cast< int * >( alloca( k * sizeof( int ) ) );
+ memset( pTmpArray, 0, k * sizeof( int ) );
+ }
+
+ if ( currentK >= k )
+ {
+ combos[ combos.AddToTail() ].CopyArray( pTmpArray, k );
+ return;
+ }
+
+ for ( int i( start ); i < n; ++i )
+ {
+ pTmpArray[ currentK ] = i;
+
+ Combinations( n, k, combos, pTmpArray, i + 1, currentK + 1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Takes an incoming Delta state, splits it's name '_' and then finds the
+// control delta (a state without a '_' in its name) and adds the index
+// of that control delta to the referenced array
+//
+// Returns true if all of the control states exist, false otherwise
+//-----------------------------------------------------------------------------
+bool CDmeMesh::GetControlDeltaIndices(
+ CDmeVertexDeltaData *pDeltaState,
+ CUtlVector< int > &controlDeltaIndices ) const
+{
+ Assert( pDeltaState );
+ return GetControlDeltaIndices( pDeltaState->GetName(), controlDeltaIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Same as above but just uses the name of a delta
+//-----------------------------------------------------------------------------
+bool CDmeMesh::GetControlDeltaIndices(
+ const char *pDeltaStateName,
+ CUtlVector< int > &controlDeltaIndices ) const
+{
+ Assert( pDeltaStateName );
+ controlDeltaIndices.RemoveAll();
+
+ const int nDeltaStateName( Q_strlen( pDeltaStateName ) );
+ char *pTmpBuf( reinterpret_cast< char * >( alloca( nDeltaStateName + 1 ) ) );
+ Q_strncpy( pTmpBuf, pDeltaStateName, nDeltaStateName + 1 );
+ char *pNext;
+ for ( char *pCurr = pTmpBuf; pCurr; pCurr = pNext )
+ {
+ pNext = strchr( pCurr, '_' );
+ if ( pNext )
+ {
+ *pNext = '\0';
+ ++pNext;
+ }
+
+ if ( Q_strlen( pCurr ) )
+ {
+ const int controlDeltaIndex( FindDeltaStateIndex( pCurr ) );
+ if ( controlDeltaIndex >= 0 )
+ {
+ controlDeltaIndices.AddToTail( controlDeltaIndex );
+ }
+ else
+ {
+ controlDeltaIndices.RemoveAll();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all of the underlying control delta indices for each
+// delta state in the mesh
+//
+// e.g. Say the delta states are (in this order): A, B, C, A_C, A_B_C
+//
+// Will build: {
+// { 0 },
+// { 1 },
+// { 2 },
+// { 0, 2 },
+// { 0, 1, 2 }
+// }
+//
+// Returns true if all of the control states exist, false otherwise
+//-----------------------------------------------------------------------------
+bool CDmeMesh::BuildCompleteDeltaStateControlList(
+ CUtlVector< CUtlVector< int > > &deltaStateControlList ) const
+{
+ deltaStateControlList.RemoveAll();
+
+ CUtlVector< int > tmpControlDeltaIndices;
+
+ const int nDeltas( m_DeltaStates.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( !GetControlDeltaIndices( m_DeltaStates[ i ], tmpControlDeltaIndices ) )
+ return false;
+
+ deltaStateControlList[ deltaStateControlList.AddToTail() ].CopyArray( tmpControlDeltaIndices.Base(), tmpControlDeltaIndices.Count() );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Searches controlList for a sub array that has exactly the same indices as
+// controlIndices. The order of the indices do not have to match but all of
+// them must be present and no extras can be present.
+// It assumes that the controlList is in the same order as m_deltaStates
+//-----------------------------------------------------------------------------
+int CDmeMesh::FindDeltaIndexFromControlIndices(
+ const CUtlVector< int > &controlIndices,
+ const CUtlVector< CUtlVector< int > > &controlList ) const
+{
+ const int nControlIndices( controlIndices.Count() );
+ const int nControlList( controlList.Count() );
+
+ int nControlListIndices;
+ int foundCount;
+
+ for ( int i = 0; i < nControlList; ++i )
+ {
+ const CUtlVector< int > &controlListIndices( controlList[ i ] );
+ nControlListIndices = controlListIndices.Count();
+ if ( nControlListIndices == nControlIndices )
+ {
+ foundCount = 0;
+
+ for ( int j( 0 ); j < nControlListIndices; ++j )
+ {
+ for ( int k( 0 ); k < nControlIndices; ++k )
+ {
+ if ( controlListIndices[ j ] == controlIndices[ k ] )
+ {
+ ++foundCount;
+ break;
+ }
+ }
+ }
+
+ if ( foundCount == nControlIndices )
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all of the required underlying deltas that make up this
+// state whether that do not exist. All of the control deltas must exist
+// though (Deltas without '_' in their name).
+//
+// e.g. Say only Delta states A, B, C, D, A_B_C_D exist and A_B_C_D is
+// passed in. This function will return:
+//
+// A_B_C, A_B_D, A_C_D, B_C_D, A_B, A_C, A_D, B_C, B_D, C_D
+//
+// Returns true if all of the control states exist, false otherwise
+//-----------------------------------------------------------------------------
+bool CDmeMesh::BuildMissingDependentDeltaList(
+ CDmeVertexDeltaData *pDeltaState,
+ CUtlVector< int > &controlIndices,
+ CUtlVector< CUtlVector< int > > &dependentStates ) const
+{
+ dependentStates.RemoveAll();
+
+ CUtlVector< CUtlVector< int > > deltaStateControlList;
+ BuildCompleteDeltaStateControlList( deltaStateControlList );
+
+ if ( !GetControlDeltaIndices( pDeltaState, controlIndices ) )
+ return false;
+
+ const int nControlIndices( controlIndices.Count() );
+
+ CUtlVector< int > comboControls;
+
+ for ( int i( nControlIndices - 1 ); i > 0; --i )
+ {
+ CUtlVector< CUtlVector< int > > combos;
+ Combinations( nControlIndices, i, combos );
+ const int nCombos( combos.Count() );
+ for ( int j( 0 ); j < nCombos; ++j )
+ {
+ const CUtlVector< int > &comboIndices( combos[ j ] );
+ const int nComboIndices( comboIndices.Count() );
+ if ( comboIndices.Count() )
+ {
+ comboControls.RemoveAll();
+ comboControls.EnsureCapacity( nComboIndices );
+
+ for ( int k( 0 ); k < nComboIndices; ++k )
+ {
+ comboControls.AddToTail( controlIndices[ comboIndices[ k ] ] );
+ }
+
+ if ( FindDeltaIndexFromControlIndices( comboControls, deltaStateControlList) < 0 )
+ {
+ dependentStates[ dependentStates.AddToTail() ].CopyArray( comboControls.Base(), comboControls.Count() );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+int CDmeMesh::GenerateCompleteDataForDelta(
+ const CDmeVertexDeltaData *pDelta,
+ T_t *pFullData,
+ int nFullData,
+ CDmeVertexData::StandardFields_t standardField )
+{
+ memset( pFullData, 0, nFullData * sizeof( T_t ) );
+
+ const FieldIndex_t fIndex( pDelta->FindFieldIndex( standardField ) );
+ if ( fIndex >= 0 )
+ {
+ CDmrArrayConst< T_t > fDataArray( pDelta->GetVertexData( fIndex ) );
+ const CUtlVector< T_t > &fData( fDataArray.Get() );
+ const CUtlVector< int > &fIndexData( pDelta->GetVertexIndexData( fIndex ) );
+ const int nIndexData( fIndexData.Count() );
+
+ Assert( nIndexData <= nFullData );
+
+ int index;
+
+ int i( 0 );
+
+ for ( int j( 0 ); j < nIndexData; ++j )
+ {
+ index = fIndexData[ j ];
+ while ( index > i )
+ {
+ ++i;
+ }
+
+ Assert( i < nFullData );
+ pFullData[ i ] = fData[ j ];
+ }
+
+ return nIndexData;
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddDelta(
+ const CDmeVertexDeltaData *pDelta,
+ T_t *pFullData,
+ int nFullData,
+ FieldIndex_t fieldIndex,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ if ( fieldIndex >= 0 )
+ {
+ CDmrArrayConst< T_t > fDataArray( pDelta->GetVertexData( fieldIndex ) );
+ const CUtlVector< T_t > &fData( fDataArray.Get() );
+ const CUtlVector< int > &fIndexData( pDelta->GetVertexIndexData( fieldIndex ) );
+ const int nIndexData( fIndexData.Count() );
+
+ T_t t;
+
+ Assert( nIndexData <= nFullData );
+
+ int index;
+
+ int i( 0 );
+
+ if ( pMask )
+ {
+ float cWeight;
+
+ for ( int j( 0 ); j < nIndexData; ++j )
+ {
+ index = fIndexData[ j ];
+
+ if ( !pMask->GetWeight( index, cWeight ) )
+ continue;
+
+ while ( index > i )
+ {
+ ++i;
+ }
+
+ Assert( i < nFullData );
+
+ t = fData[ j ];
+ t *= ( weight * cWeight );
+ pFullData[ i ] += t;
+ }
+ }
+ else
+ {
+ for ( int j( 0 ); j < nIndexData; ++j )
+ {
+ index = fIndexData[ j ];
+ while ( index > i )
+ {
+ ++i;
+ }
+
+ Assert( i < nFullData );
+ t = fData[ j ];
+ t *= weight;
+ pFullData[ i ] += t;
+ }
+ }
+ }
+}
+
+template void CDmeMesh::AddDelta< float >( const CDmeVertexDeltaData *, float *, int, FieldIndex_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector2D >( const CDmeVertexDeltaData *, Vector2D *, int, FieldIndex_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector >( const CDmeVertexDeltaData *, Vector *, int, FieldIndex_t, float, const CDmeSingleIndexedComponent * );
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddDelta(
+ const CDmeVertexDeltaData *pDelta,
+ T_t *pFullData,
+ int nFullData,
+ CDmeVertexData::StandardFields_t standardField,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ const FieldIndex_t fIndex( pDelta->FindFieldIndex( standardField ) );
+ AddDelta( pDelta, pFullData, nFullData, fIndex, weight, pMask );
+}
+
+template void CDmeMesh::AddDelta< float >( const CDmeVertexDeltaData *, float *, int, CDmeVertexData::StandardFields_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector2D >( const CDmeVertexDeltaData *, Vector2D *, int, CDmeVertexData::StandardFields_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector >( const CDmeVertexDeltaData *, Vector *, int, CDmeVertexData::StandardFields_t, float, const CDmeSingleIndexedComponent * );
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeAllCorrectedPositionsFromActualPositions()
+{
+ const CDmeVertexData *pBase = GetBindBaseState();
+ if ( !pBase )
+ return;
+
+ CUtlVector< DeltaComputation_t > deltaList;
+ ComputeDependentDeltaStateList( deltaList );
+
+ const int nDeltas( deltaList.Count() );
+
+ const int nPositions( pBase->GetPositionData().Count() );
+
+ Vector *pPositions( reinterpret_cast< Vector * >( alloca( nPositions * sizeof( Vector ) ) ) );
+ int *pIndices( reinterpret_cast< int * >( alloca( nPositions * sizeof( int ) ) ) );
+
+ int pCount;
+
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ const DeltaComputation_t &deltaComputation( deltaList[ i ] );
+ CDmeVertexDeltaData *pDelta( m_DeltaStates[ deltaComputation.m_nDeltaIndex ] );
+ if ( !pDelta->GetValue< bool >( "corrected" ) )
+ {
+ const FieldIndex_t pIndex( pDelta->FindFieldIndex( CDmeVertexDeltaData::FIELD_POSITION ) );
+ if ( pIndex < 0 )
+ continue;
+
+ GenerateCompleteDataForDelta( pDelta, pPositions, nPositions, CDmeVertexData::FIELD_POSITION );
+
+ const CUtlVector< int > &dependentDeltas( deltaComputation.m_DependentDeltas );
+ const int nDependentDeltas( dependentDeltas.Count() );
+ for ( int j( 0 ); j < nDependentDeltas; ++j )
+ {
+ const CDmeVertexDeltaData *pDependentDelta( m_DeltaStates[ dependentDeltas[ j ] ] );
+ const CUtlVector< Vector > &dPositions( pDependentDelta->GetPositionData() );
+ const CUtlVector<int> &dIndices( pDependentDelta->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) );
+ Assert( dPositions.Count() == dIndices.Count() );
+ const int nIndices( dIndices.Count() );
+
+ int index;
+
+ int k( 0 );
+ for ( int l( 0 ); l < nIndices; ++l )
+ {
+ index = dIndices[ l ];
+ while ( index > k )
+ {
+ ++k;
+ }
+
+ Assert( k < nPositions );
+ pPositions[ k ] -= dPositions[ l ];
+ }
+ }
+
+ pCount = 0;
+ for ( int j( 0 ); j < nPositions; ++j )
+ {
+ const Vector &v( pPositions[ j ] );
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ pPositions[ pCount ] = v;
+ pIndices[ pCount ] = j;
+ ++pCount;
+ }
+ }
+
+ pDelta->RemoveAllVertexData( pIndex );
+
+ if ( pCount )
+ {
+ pDelta->AddVertexData( pIndex, pCount );
+ pDelta->SetVertexData( pIndex, 0, pCount, AT_VECTOR3, pPositions );
+ pDelta->SetVertexIndices( pIndex, 0, pCount, pIndices );
+ }
+ pDelta->SetValue( "corrected", true );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// There's no guarantee that fields are added in any order, nor that only
+// standard fields exist...
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddCorrectedDelta(
+ CDmrArray< T_t > &baseDataArray,
+ const CUtlVector< int > &baseIndices,
+ const DeltaComputation_t &deltaComputation,
+ const char *pFieldName,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ const CUtlVector< T_t > &baseData( baseDataArray.Get() );
+ const int nData( baseData.Count() );
+ T_t *pData( reinterpret_cast< T_t * >( alloca( nData * sizeof( T_t ) ) ) );
+ Q_memcpy( pData, baseData.Base(), nData * sizeof( T_t ) );
+
+ CDmeVertexDeltaData *pDelta( GetDeltaState( deltaComputation.m_nDeltaIndex ) );
+
+ const int deltaFieldIndex( pDelta->FindFieldIndex( pFieldName ) );
+ if ( deltaFieldIndex < 0 )
+ return;
+
+ AddDelta( pDelta, pData, nData, deltaFieldIndex, weight, pMask );
+
+ const CUtlVector< int > &depDeltas( deltaComputation.m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pDelta = GetDeltaState( depDeltas[ j ] );
+
+ int depFieldIndex = pDelta->FindFieldIndex( pFieldName );
+ if ( depFieldIndex < 0 )
+ continue;
+
+ AddDelta( pDelta, pData, nData, depFieldIndex, weight, pMask );
+ }
+
+ baseDataArray.CopyArray( pData, nData );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddCorrectedDelta(
+ CUtlVector< T_t > &baseData,
+ const CUtlVector< int > &baseIndices,
+ const DeltaComputation_t &deltaComputation,
+ const char *pFieldName,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ const int nData( baseData.Count() );
+
+ CDmeVertexDeltaData *pDelta( GetDeltaState( deltaComputation.m_nDeltaIndex ) );
+
+ const int deltaFieldIndex( pDelta->FindFieldIndex( pFieldName ) );
+ if ( deltaFieldIndex < 0 )
+ return;
+
+ AddDelta( pDelta, baseData.Base(), nData, deltaFieldIndex, weight, pMask );
+
+ const CUtlVector< int > &depDeltas( deltaComputation.m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pDelta = GetDeltaState( depDeltas[ j ] );
+
+ int depFieldIndex = pDelta->FindFieldIndex( pFieldName );
+ if ( depFieldIndex < 0 )
+ continue;
+
+ AddDelta( pDelta, baseData.Base(), nData, depFieldIndex, weight, pMask );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// There's no guarantee that fields are added in any order, nor that only
+// standard fields exist...
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddRawDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmrArray< T_t > &baseDataArray,
+ FieldIndex_t nDeltaFieldIndex,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ if ( !pDelta || nDeltaFieldIndex < 0 )
+ return;
+
+ const CUtlVector< T_t > &baseData( baseDataArray.Get() );
+ const int nData( baseData.Count() );
+ T_t *pData( reinterpret_cast< T_t * >( alloca( nData * sizeof( T_t ) ) ) );
+ Q_memcpy( pData, baseData.Base(), nData * sizeof( T_t ) );
+
+ AddDelta( pDelta, pData, nData, nDeltaFieldIndex, weight, pMask );
+
+ baseDataArray.CopyArray( pData, nData );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddRawDelta(
+ CDmeVertexDeltaData *pDelta,
+ CUtlVector< T_t > &baseData,
+ FieldIndex_t nDeltaFieldIndex,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ if ( !pDelta || nDeltaFieldIndex < 0 )
+ return;
+
+ const int nData( baseData.Count() );
+
+ AddDelta( pDelta, baseData.Base(), nData, nDeltaFieldIndex, weight, pMask );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the specified base state to the specified delta
+// If no delta is specified then the current state is copied from the bind state
+// If no base state is specified then the current base state is used
+// The specified base state or the current base state cannot be the bind state
+//-----------------------------------------------------------------------------
+bool CDmeMesh::SetBaseStateToDelta( const CDmeVertexDeltaData *pDelta, CDmeVertexData *pPassedBase /* = NULL */ )
+{
+ CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ const CDmeVertexData *pBind = GetBindBaseState();
+
+ if ( !pBase || !pBind || pBase == pBind )
+ return false;
+
+ pBind->CopyTo( pBase );
+
+ if ( !pDelta )
+ return true;
+
+ // This should be cached and recomputed only when states are added
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDependentDeltaStateList( compList );
+
+ const int nDeltas( compList.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( pDelta != GetDeltaState( compList[ i ].m_nDeltaIndex ) )
+ continue;
+
+ const int nBaseField( pBase->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pBase->FieldName( j ) );
+
+ for ( int k( 0 ); k < nDeltaField; ++k )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( k ) );
+
+ if ( baseFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+ if ( baseFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ const CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pBaseData->GetType() != pDeltaData->GetType() )
+ break;
+
+ const CUtlVector< int > &baseIndices( pBase->GetVertexIndexData( baseFieldIndex ) );
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AddCorrectedDelta( CDmrArray< float >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ case AT_COLOR_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector2D >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SelectVerticesFromDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeSingleIndexedComponent *pSelection )
+{
+ if ( !pSelection )
+ return;
+
+ pSelection->Clear();
+
+ if ( !pDelta )
+ return;
+
+ const FieldIndex_t pField( pDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pField < 0 )
+ return;
+
+ const CUtlVector< int > &pIndicies( pDelta->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) );
+
+ pSelection->AddComponents( pIndicies );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SelectAllVertices( CDmeSingleIndexedComponent *pSelection, CDmeVertexData *pPassedBase /* = NULL */ )
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+
+ if ( !pBase )
+ {
+ pBase = GetBindBaseState();
+ }
+
+ if ( !pBase )
+ return;
+
+ if ( !pSelection )
+ return;
+
+ pSelection->Clear();
+
+ const FieldIndex_t pField( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pField < 0 )
+ return;
+
+ CUtlVector< int > indices;
+ indices.EnsureCount( CDmrArrayConst< Vector >( pBase->GetVertexData( pField ) ).Count() );
+ const int nIndices = indices.Count();
+ for ( int i = 0; i < nIndices; ++i )
+ {
+ indices[ i ] = i;
+ }
+
+ pSelection->AddComponents( indices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SelectHalfVertices( SelectHalfType_t selectHalfType, CDmeSingleIndexedComponent *pSelection, CDmeVertexData *pPassedBase /* = NULL */ )
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+
+ if ( !pBase )
+ {
+ pBase = GetBindBaseState();
+ }
+
+ if ( !pBase )
+ return;
+
+ if ( !pSelection )
+ return;
+
+ pSelection->Clear();
+
+ const FieldIndex_t pField( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pField < 0 )
+ return;
+
+ const CDmrArrayConst< Vector > pos( pBase->GetVertexData( pField ) );
+ const int nPosCount = pos.Count();
+
+ CUtlVector< int > indices;
+ indices.EnsureCapacity( nPosCount );
+
+ if ( selectHalfType == kRight )
+ {
+ for ( int i = 0; i < nPosCount; ++i )
+ {
+ if ( pos[ i ].x <= 0.0f )
+ {
+ indices.AddToTail( i );
+ }
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nPosCount; ++i )
+ {
+ if ( pos[ i ].x >= 0.0f )
+ {
+ indices.AddToTail( i );
+ }
+ }
+ }
+
+ pSelection->AddComponents( indices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::CreateDeltaFieldFromBaseField(
+ CDmeVertexData::StandardFields_t nStandardFieldIndex,
+ const CDmrArrayConst< float > &baseArray,
+ const CDmrArrayConst< float > &bindArray,
+ CDmeVertexDeltaData *pDelta )
+{
+ const int nData( baseArray.Count() );
+ if ( nData != bindArray.Count() )
+ return false;
+
+ const float *pBaseData( baseArray.Get().Base() );
+ const float *pBindData( bindArray.Get().Base() );
+
+ float *pData( reinterpret_cast< float * >( nData * sizeof( float ) ) );
+ Q_memcpy( pData, pBaseData, nData * sizeof( float ) );
+ int *pIndices( reinterpret_cast< int * >( nData * sizeof( int ) ) );
+
+ float v;
+
+ int nDeltaCount( 0 );
+ for ( int i = 0; i < nData; ++i )
+ {
+ v = pBaseData[ i ] - pBindData[ i ];
+
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v ) >= ( 1 / 4096.0f ) )
+ {
+ pData[ nDeltaCount ] = v;
+ pIndices[ nDeltaCount ] = i;
+ ++nDeltaCount;
+ }
+ }
+
+ if ( nDeltaCount <= 0 )
+ return true;
+
+ FieldIndex_t fieldIndex( pDelta->CreateField( nStandardFieldIndex ) );
+ if ( fieldIndex < 0 )
+ return false;
+
+ pDelta->AddVertexData( fieldIndex, nDeltaCount );
+ pDelta->SetVertexData( fieldIndex, 0, nDeltaCount, AT_FLOAT, pData );
+ pDelta->SetVertexIndices( fieldIndex, 0, nDeltaCount, pIndices );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::CreateDeltaFieldFromBaseField(
+ CDmeVertexData::StandardFields_t nStandardFieldIndex,
+ const CDmrArrayConst< Vector2D > &baseArray,
+ const CDmrArrayConst< Vector2D > &bindArray,
+ CDmeVertexDeltaData *pDelta )
+{
+ const int nData( baseArray.Count() );
+ if ( nData != bindArray.Count() )
+ return false;
+
+ const Vector2D *pBaseData( baseArray.Get().Base() );
+ const Vector2D *pBindData( bindArray.Get().Base() );
+
+ Vector2D *pData( reinterpret_cast< Vector2D * >( nData * sizeof( Vector2D ) ) );
+ Q_memcpy( pData, pBaseData, nData * sizeof( Vector2D ) );
+ int *pIndices( reinterpret_cast< int * >( nData * sizeof( int ) ) );
+
+ Vector2D v;
+
+ int nDeltaCount( 0 );
+ for ( int i = 0; i < nData; ++i )
+ {
+ v = pBaseData[ i ] - pBindData[ i ];
+
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) )
+ {
+ pData[ nDeltaCount ] = v;
+ pIndices[ nDeltaCount ] = i;
+ ++nDeltaCount;
+ }
+ }
+
+ if ( nDeltaCount <= 0 )
+ return true;
+
+ FieldIndex_t fieldIndex( pDelta->CreateField( nStandardFieldIndex ) );
+ if ( fieldIndex < 0 )
+ return false;
+
+ pDelta->AddVertexData( fieldIndex, nDeltaCount );
+ pDelta->SetVertexData( fieldIndex, 0, nDeltaCount, AT_VECTOR2, pData );
+ pDelta->SetVertexIndices( fieldIndex, 0, nDeltaCount, pIndices );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::CreateDeltaFieldFromBaseField(
+ CDmeVertexData::StandardFields_t nStandardFieldIndex,
+ const CDmrArrayConst< Vector > &baseArray,
+ const CDmrArrayConst< Vector > &bindArray,
+ CDmeVertexDeltaData *pDelta )
+{
+ const int nData( baseArray.Count() );
+ if ( nData != bindArray.Count() )
+ return false;
+
+ const Vector *pBaseData( baseArray.Get().Base() );
+ const Vector *pBindData( bindArray.Get().Base() );
+
+ Vector *pData( reinterpret_cast< Vector * >( alloca( nData * sizeof( Vector ) ) ) );
+ Q_memcpy( pData, pBaseData, nData * sizeof( Vector ) );
+ int *pIndices( reinterpret_cast< int * >( alloca( nData * sizeof( int ) ) ) );
+
+ Vector v;
+
+ int nDeltaCount( 0 );
+ for ( int i = 0; i < nData; ++i )
+ {
+ v = pBaseData[ i ] - pBindData[ i ];
+
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ pData[ nDeltaCount ] = v;
+ pIndices[ nDeltaCount ] = i;
+ ++nDeltaCount;
+ }
+ }
+
+ if ( nDeltaCount <= 0 )
+ return true;
+
+ FieldIndex_t fieldIndex( pDelta->CreateField( nStandardFieldIndex ) );
+ if ( fieldIndex < 0 )
+ return false;
+
+ pDelta->AddVertexData( fieldIndex, nDeltaCount );
+ pDelta->SetVertexData( fieldIndex, 0, nDeltaCount, AT_VECTOR3, pData );
+ pDelta->SetVertexIndices( fieldIndex, 0, nDeltaCount, pIndices );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Creates a delta from the difference between the bind base state and the
+// specified base state. If pBaseName is NULL the current base state is used
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::ModifyOrCreateDeltaStateFromBaseState( const char *pDeltaName, CDmeVertexData *pPassedBase /* = NULL */, bool absolute /* = false */ )
+{
+ // Find All States Which Have This Guy
+ CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return NULL;
+
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return NULL;
+
+ // It's ok if pBase == pBind
+
+ CUtlVector< int > superiorDeltaStates;
+ ComputeSuperiorDeltaStateList( pDeltaName, superiorDeltaStates );
+ const int nSuperior = superiorDeltaStates.Count();
+
+ if ( nSuperior > 0 )
+ {
+ UniqueId_t id;
+ char idBuf[ MAX_PATH ];
+
+ CDmeVertexData *pTmpBaseState = NULL;
+ do
+ {
+ CreateUniqueId( &id );
+ UniqueIdToString( id, idBuf, sizeof( idBuf ) );
+ pTmpBaseState = FindBaseState( idBuf );
+ } while( pTmpBaseState != NULL );
+
+ pTmpBaseState = FindOrCreateBaseState( idBuf );
+ if ( !pTmpBaseState )
+ return NULL;
+
+ for ( int i = 0; i < nSuperior; ++i )
+ {
+ Assert( superiorDeltaStates[ i ] < DeltaStateCount() );
+ CDmeVertexDeltaData *pSuperiorDelta = GetDeltaState( superiorDeltaStates[ i ] );
+ if ( pSuperiorDelta->GetValue< bool >( "corrected" ) )
+ {
+ // Only fiddle with states that are "corrected"
+ if ( !SetBaseStateToDelta( pSuperiorDelta, pTmpBaseState ) )
+ return NULL;
+
+ if ( !ModifyOrCreateDeltaStateFromBaseState( CUtlString( pSuperiorDelta->GetName() ), pTmpBaseState, true ) )
+ return NULL;
+ }
+ }
+
+ DeleteBaseState( idBuf );
+ }
+
+ ResetDeltaState( pDeltaName );
+ CDmeVertexDeltaData *pDelta = FindOrCreateDeltaState( pDeltaName );
+ if ( !pDelta )
+ return NULL;
+
+ CDmeVertexData::StandardFields_t deltaFields[] =
+ {
+ CDmeVertexData::FIELD_POSITION,
+ CDmeVertexData::FIELD_NORMAL,
+ CDmeVertexData::FIELD_WRINKLE
+ };
+
+ for ( int i = 0; i < sizeof( deltaFields ) / sizeof( deltaFields[ 0 ] ); ++i )
+ {
+ CDmeVertexData::StandardFields_t standardFieldIndex( deltaFields[ i ] );
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( standardFieldIndex ) );
+ const FieldIndex_t bindFieldIndex( pBind->FindFieldIndex( standardFieldIndex ) );
+
+ if ( baseFieldIndex < 0 || bindFieldIndex < 0 )
+ continue;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pBindData( pBind->GetVertexData( bindFieldIndex ) );
+
+ if ( pBaseData->GetType() != pBindData->GetType() )
+ continue;
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< float >( pBaseData ), CDmrArrayConst< float >( pBindData ), pDelta );
+ break;
+ case AT_COLOR_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< Vector >( pBaseData ), CDmrArrayConst< Vector >( pBindData ), pDelta );
+ break;
+ case AT_VECTOR2_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< Vector2D >( pBaseData ), CDmrArrayConst< Vector2D >( pBindData ), pDelta );
+ break;
+ case AT_VECTOR3_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< Vector >( pBaseData ), CDmrArrayConst< Vector >( pBindData ), pDelta );
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ( !strchr( pDelta->GetName(), '_' ) )
+ {
+ const static UtlSymId_t symTargets = g_pDataModel->GetSymbol( "targets" );
+ CDmeCombinationOperator *pCombo( FindReferringElement< CDmeCombinationOperator >( this, symTargets ) );
+ if ( pCombo )
+ {
+ pCombo->FindOrCreateControl( pDelta->GetName(), false, true );
+ }
+ }
+
+ if ( !absolute )
+ {
+ ComputeAllCorrectedPositionsFromActualPositions();
+ }
+
+ return pDelta;
+}
+
+
+//-----------------------------------------------------------------------------
+// TODO: Uncorrect all superior states and then correct them afterwards
+//-----------------------------------------------------------------------------
+bool CDmeMesh::DeleteDeltaState( const char *pDeltaName )
+{
+ const int nDeltaIndex = FindDeltaStateIndex( pDeltaName );
+ if ( nDeltaIndex < 0 )
+ return false;
+
+ Assert( m_DeltaStates.Count() == m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ].Count() );
+ Assert( m_DeltaStates.Count() == m_DeltaStateWeights[ MESH_DELTA_WEIGHT_LAGGED ].Count() );
+ CDmeVertexDeltaData *pDelta( m_DeltaStates[ nDeltaIndex ] );
+ if ( !pDelta )
+ return false;
+
+ m_DeltaStates.Remove( nDeltaIndex );
+ m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ].Remove( nDeltaIndex );
+ m_DeltaStateWeights[ MESH_DELTA_WEIGHT_LAGGED ].Remove( nDeltaIndex );
+ g_pDataModel->DestroyElement( pDelta->GetHandle() );
+
+ const static UtlSymId_t symTargets = g_pDataModel->GetSymbol( "targets" );
+ CDmeCombinationOperator *pCombo( FindReferringElement< CDmeCombinationOperator >( this, symTargets ) );
+ if ( pCombo )
+ {
+ pCombo->Purge();
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// TODO: Uncorrect all superior states and then correct them afterwards
+//-----------------------------------------------------------------------------
+bool CDmeMesh::ResetDeltaState( const char *pDeltaName )
+{
+ const int nDeltaIndex = FindDeltaStateIndex( pDeltaName );
+ if ( nDeltaIndex < 0 )
+ return false;
+
+ CDmeVertexDeltaData *pOldDelta = m_DeltaStates[ nDeltaIndex ];
+ CDmeVertexDeltaData *pNewDelta = CreateElement< CDmeVertexDeltaData >( pOldDelta->GetName(), GetFileId() );
+ if ( !pNewDelta )
+ return false;
+
+ m_DeltaStates.Set( nDeltaIndex, pNewDelta );
+ g_pDataModel->DestroyElement( pOldDelta->GetHandle() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+class CSelectionHelper
+{
+public:
+ class CVert
+ {
+ public:
+ int m_index;
+ int m_count;
+ float m_weight;
+ };
+
+ void AddVert( int vIndex, float weight = 1.0f );
+
+ int AddToSelection( CDmeSingleIndexedComponent *pSelection ) const;
+
+ int RemoveFromSelection( CDmeSingleIndexedComponent *pSelection, bool bAllowEmpty ) const;
+
+protected:
+ CUtlVector< CVert > m_verts;
+
+ int BinarySearch( int component ) const;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CSelectionHelper::AddVert( int vIndex, float weight /* = 1.0f */ )
+{
+ // Find the vertex, add it if necessary
+ const int index = BinarySearch( vIndex );
+
+ if ( index == m_verts.Count() )
+ {
+ // New Add to end
+ CVert &v( m_verts[ m_verts.AddToTail() ] );
+ v.m_index = vIndex;
+ v.m_count = 1;
+ v.m_weight = weight;
+ }
+ else if ( vIndex == m_verts[ index ].m_index )
+ {
+ // Existing, increment
+ CVert &v( m_verts[ index ] );
+ Assert( v.m_index == vIndex );
+ v.m_count += 1;
+ v.m_weight += weight;
+ }
+ else
+ {
+ // New insert before index
+ CVert &v( m_verts[ m_verts.InsertBefore( index ) ] );
+ v.m_index = vIndex;
+ v.m_count = 1;
+ v.m_weight = weight;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CSelectionHelper::AddToSelection( CDmeSingleIndexedComponent *pSelection ) const
+{
+ const int nVerts = m_verts.Count();
+
+ for ( int i = 0; i < nVerts; ++i )
+ {
+ const CVert &v( m_verts[ i ] );
+ Assert( !pSelection->HasComponent( v.m_index ) );
+ pSelection->AddComponent( v.m_index, v.m_weight / static_cast< float >( v.m_count ) );
+ }
+
+ return nVerts;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CSelectionHelper::RemoveFromSelection( CDmeSingleIndexedComponent *pSelection, bool bAllowEmpty ) const
+{
+ const int nVerts = m_verts.Count();
+ int nVertsRemovedCount = 0;
+
+ for ( int i = 0; i < nVerts; ++i )
+ {
+ const CVert &v( m_verts[ i ] );
+ if ( bAllowEmpty || pSelection->Count() > 1 )
+ {
+ pSelection->RemoveComponent( v.m_index );
+ ++nVertsRemovedCount;
+ }
+ }
+
+ return nVertsRemovedCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Searches for the component in the sorted component list and returns the
+// index if it's found or if it's not found, returns the index at which it
+// should be inserted to maintain the sorted order of the component list
+//-----------------------------------------------------------------------------
+int CSelectionHelper::BinarySearch( int vIndex ) const
+{
+ const int nVerts( m_verts.Count() );
+
+ int left( 0 );
+ int right( nVerts - 1 );
+ int mid;
+
+ while ( left <= right )
+ {
+ mid = ( left + right ) >> 1; // floor( ( left + right ) / 2.0 )
+ if ( vIndex > m_verts[ mid ].m_index )
+ {
+ left = mid + 1;
+ }
+ else if ( vIndex < m_verts[ mid ].m_index )
+ {
+ right = mid - 1;
+ }
+ else
+ {
+ return mid;
+ }
+ }
+
+ return left;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::GrowSelection( int nSize, CDmeSingleIndexedComponent *pSelection, CDmMeshComp *pPassedMeshComp )
+{
+ if ( nSize <= 0 || !pSelection )
+ return;
+
+ CUtlVector< int > sIndices;
+ CUtlVector< float > sWeights;
+ pSelection->GetComponents( sIndices, sWeights );
+ const int nVertices = sIndices.Count();
+
+ CDmMeshComp *pMeshComp = pPassedMeshComp ? pPassedMeshComp : new CDmMeshComp( this );
+
+ CUtlVector< CDmMeshComp::CVert * > neighbours;
+
+ CSelectionHelper sHelper;
+
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ const int nNeighbours = pMeshComp->FindNeighbouringVerts( sIndices[ i ], neighbours );
+ for ( int j = 0; j < nNeighbours; ++j )
+ {
+ CDmMeshComp::CVert *pNeighbour = neighbours[ j ];
+ Assert( pNeighbour );
+ if ( pNeighbour )
+ {
+ const int vIndex = pNeighbour->PositionIndex();
+ if ( !pSelection->HasComponent( vIndex ) )
+ {
+ sHelper.AddVert( vIndex, sWeights[ i ] );
+ }
+ }
+ }
+ }
+
+ if ( sHelper.AddToSelection( pSelection ) > 0 )
+ {
+ GrowSelection( nSize - 1, pSelection, pMeshComp );
+ }
+
+ if ( pMeshComp != pPassedMeshComp )
+ {
+ delete pMeshComp;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::ShrinkSelection( int nSize, CDmeSingleIndexedComponent *pSelection, CDmMeshComp *pPassedMeshComp )
+{
+ if ( nSize <= 0 || !pSelection )
+ return;
+
+ CUtlVector< int > sIndices;
+ CUtlVector< float > sWeights;
+ pSelection->GetComponents( sIndices, sWeights );
+ const int nVertices = sIndices.Count();
+
+ CDmMeshComp *pMeshComp = pPassedMeshComp ? pPassedMeshComp : new CDmMeshComp( this );
+
+ CUtlVector< CDmMeshComp::CVert * > neighbours;
+
+ CSelectionHelper sHelper;
+
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ bool hasSelectedNeighbour = false;
+ bool hasUnselectedNeighbour = false;
+
+ const int vIndex = sIndices[ i ];
+ const int nNeighbours = pMeshComp->FindNeighbouringVerts( vIndex, neighbours );
+ for ( int j = 0; j < nNeighbours; ++j )
+ {
+ const int nvIndex = neighbours[ j ]->PositionIndex();
+ if ( pSelection->HasComponent( nvIndex ) )
+ {
+ hasSelectedNeighbour = true;
+ if ( hasUnselectedNeighbour )
+ {
+ sHelper.AddVert( vIndex );
+ break;
+ }
+ }
+ else
+ {
+ hasUnselectedNeighbour = true;
+ if ( hasSelectedNeighbour )
+ {
+ sHelper.AddVert( vIndex );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( sHelper.RemoveFromSelection( pSelection, false ) > 0 )
+ {
+ ShrinkSelection( nSize - 1, pSelection, pMeshComp );
+ }
+
+ if ( pMeshComp != pPassedMeshComp )
+ {
+ delete pMeshComp;
+ }
+}
+
+
+CDmeSingleIndexedComponent *CDmeMesh::FeatherSelection(
+ float falloffDistance,
+ Falloff_t falloffType,
+ Distance_t distanceType,
+ CDmeSingleIndexedComponent *pSelection,
+ CDmMeshComp *pPassedMeshComp )
+{
+ switch ( falloffType )
+ {
+ case SMOOTH:
+ return FeatherSelection< SMOOTH >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ case SPIKE:
+ return FeatherSelection< SPIKE >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ case DOME:
+ return FeatherSelection< DOME >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ default:
+ return FeatherSelection< LINEAR >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < int T >
+CDmeSingleIndexedComponent *CDmeMesh::FeatherSelection(
+ float fDistance, Distance_t distanceType,
+ CDmeSingleIndexedComponent *pSelection, CDmMeshComp *pPassedMeshComp )
+{
+ // TODO: Support feathering inward instead of just outward
+ if ( fDistance <= 0.0f || !pSelection )
+ return NULL;
+
+ // Make a new CDmeSingleIndexedComponent to do all of the dirty work
+ CDmeSingleIndexedComponent *pNewSelection = CreateElement< CDmeSingleIndexedComponent >( "feather", pSelection->GetFileId() );
+ pSelection->CopyAttributesTo( pNewSelection );
+
+ CDmMeshComp *pMeshComp = pPassedMeshComp ? pPassedMeshComp : new CDmMeshComp( this );
+ CDmeVertexData *pBase = pMeshComp->BaseState();
+
+ if ( distanceType == DIST_RELATIVE )
+ {
+ Vector vCenter;
+ float flRadius;
+ GetBoundingSphere( vCenter, flRadius, pBase, pSelection );
+ fDistance *= flRadius;
+ }
+
+ const CUtlVector< Vector > &positions( pBase->GetPositionData() );
+ const int nPositions = positions.Count();
+
+ if ( !pBase )
+ return NULL;
+
+ CUtlVector< int > sIndices;
+
+ int insideCount = 0;
+
+ CFalloff< T > falloff;
+
+ do
+ {
+ insideCount = 0;
+ CUtlVector< CDmMeshComp::CVert * > neighbours;
+ CSelectionHelper sHelper;
+
+ pNewSelection->GetComponents( sIndices );
+ int nVertices = sIndices.Count();
+
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ const int nNeighbours = pMeshComp->FindNeighbouringVerts( sIndices[ i ], neighbours );
+
+ for ( int j = 0; j < nNeighbours; ++j )
+ {
+ const int vIndex = neighbours[ j ]->PositionIndex();
+
+ if ( pNewSelection->HasComponent( vIndex ) )
+ continue;
+
+ const int closestVert = ClosestSelectedVertex( vIndex, pSelection, pBase );
+ if ( closestVert < 0 || closestVert >= nPositions )
+ continue;
+
+ const float vDistance = positions[ vIndex ].DistTo( positions[ closestVert ] );
+ if ( vDistance <= fDistance )
+ {
+ sHelper.AddVert( vIndex, falloff( vDistance / fDistance ) );
+ ++insideCount;
+ }
+ }
+ }
+
+ sHelper.AddToSelection( pNewSelection );
+
+ } while ( insideCount > 0 );
+
+ return pNewSelection;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add the specified delta, scaled by the weight value to the DmeVertexData
+// base state specified. Optionally the add can be masked by a specified
+// weight map.
+//
+// If a DmeVertexData is not explicitly specified, the current state of the
+// mesh is modified unless it's the bind state. The bind state will never
+// be modified even if it is explicitly specified.
+//
+// Only the delta specified is added. No dependent states are added.
+//-----------------------------------------------------------------------------
+bool CDmeMesh::AddMaskedDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeVertexData *pDst /* = NULL */,
+ float weight /* = 1.0f */,
+ const CDmeSingleIndexedComponent *pMask /* = NULL */ )
+{
+ CDmeVertexData *pBase = pDst ? pDst : GetCurrentBaseState();
+
+ if ( !pBase || pBase == GetBindBaseState() )
+ return false;
+
+ bool retVal = true;
+
+ const int nBaseField( pBase->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ // Try to add every field of the base state
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pBase->FieldName( j ) );
+
+ // Find the corresponding field in the delta
+ for ( int k( 0 ); k < nDeltaField; ++k )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( k ) );
+
+ if ( baseFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+ if ( baseFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pBaseData->GetType() != pDeltaData->GetType() )
+ break;
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AddRawDelta( pDelta, CDmrArray< float >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ // TODO: Color is missing some algebraic operators
+// AddRawDelta( pDelta, CDmrArray< Color >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AddRawDelta( pDelta, CDmrArray< Vector2D >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AddRawDelta( pDelta, CDmrArray< Vector >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add the specified delta, scaled by the weight value to the DmeVertexData
+// base state specified. Optionally the add can be masked by a specified
+// weight map.
+//
+// If a DmeVertexData is not explicitly specified, the current state of the
+// mesh is modified unless it's the bind state. The bind state will never
+// be modified even if it is explicitly specified.
+//
+// Only the delta specified is added. No dependent states are added.
+//-----------------------------------------------------------------------------
+bool CDmeMesh::AddCorrectedMaskedDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeVertexData *pDst /* = NULL */,
+ float weight /* = 1.0f */,
+ const CDmeSingleIndexedComponent *pMask /* = NULL */ )
+{
+ CDmeVertexData *pBase = pDst ? pDst : GetCurrentBaseState();
+
+ if ( !pBase || pBase == GetBindBaseState() )
+ return false;
+
+ bool retVal = true;
+
+ const int nBaseField( pBase->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ // This should be cached and recomputed only when states are added
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDependentDeltaStateList( compList );
+
+ const int nDeltas( compList.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( pDelta != GetDeltaState( compList[ i ].m_nDeltaIndex ) )
+ continue;
+
+ // Try to add every field of the base state
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pBase->FieldName( j ) );
+
+ // Find the corresponding field in the delta
+ for ( int k( 0 ); k < nDeltaField; ++k )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( k ) );
+
+ if ( baseFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+ if ( baseFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pBaseData->GetType() != pDeltaData->GetType() )
+ break;
+
+ const CUtlVector< int > &baseIndices( pBase->GetVertexIndexData( baseFieldIndex ) );
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AddCorrectedDelta( CDmrArray< float >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector2D >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Interpolates between two arrays of values and stores the result in a
+// CDmrArray.
+//
+// result = ( ( 1 - weight ) * a ) + ( weight * b )
+//
+//-----------------------------------------------------------------------------
+template< class T_t >
+bool CDmeMesh::InterpMaskedData(
+ CDmrArray< T_t > &aData,
+ const CUtlVector< T_t > &bData,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask ) const
+{
+ const int nDst = aData.Count();
+
+ if ( bData.Count() != nDst )
+ return false;
+
+ // The wacky way of writing these expression is because Vector4D is missing operators
+ // And this probably works better because of fewer temporaries
+
+ T_t a;
+ T_t b;
+
+ if ( pMask )
+ {
+ // With a weight mask
+ float vWeight;
+ for ( int i = 0; i < nDst; ++i )
+ {
+ if ( pMask->GetWeight( i, vWeight ) )
+ {
+ vWeight *= weight; // Specifically not clamping
+ a = aData.Get( i );
+ a *= ( 1.0f - vWeight );
+ b = bData[ i ];
+ b *= vWeight;
+ b += a;
+ aData.Set( i, b );
+ }
+ }
+ }
+ else
+ {
+ // Without a weight mask
+ const float oneMinusWeight( 1.0f - weight );
+ for ( int i = 0; i < nDst; ++i )
+ {
+ a = aData.Get( i );
+ a *= oneMinusWeight;
+ b = bData[ i ];
+ b *= weight;
+ b += a;
+ aData.Set( i, b );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Interpolates between two CDmeVertexData's
+//
+// paData = ( ( 1 - weight ) * a ) + ( weight * b )
+//-----------------------------------------------------------------------------
+bool CDmeMesh::InterpMaskedData(
+ CDmeVertexData *paData,
+ const CDmeVertexData *pbData,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask ) const
+{
+ if ( !paData || !pbData || paData == pbData )
+ return false;
+
+ const int naField = paData->FieldCount();
+ const int nbField = pbData->FieldCount();
+
+ for ( int i = 0; i < naField; ++i )
+ {
+ const CUtlString &aFieldName( paData->FieldName( i ) );
+
+ for ( int j = 0; j < nbField; ++j )
+ {
+ const CUtlString &bFieldName( pbData->FieldName( j ) );
+ if ( aFieldName != bFieldName )
+ continue;
+
+ const FieldIndex_t aFieldIndex( paData->FindFieldIndex( aFieldName ) );
+ const FieldIndex_t bFieldIndex( pbData->FindFieldIndex( bFieldName ) );
+
+ if ( aFieldIndex < 0 || bFieldIndex < 0 )
+ break;
+
+ CDmAttribute *paAttr( paData->GetVertexData( aFieldIndex ) );
+ const CDmAttribute *pbAttr( pbData->GetVertexData( bFieldIndex ) );
+
+ if ( paAttr->GetType() != pbAttr->GetType() )
+ break;
+
+ if ( paData->GetVertexIndexData( aFieldIndex ).Count() != pbData->GetVertexIndexData( bFieldIndex ).Count() )
+ break;
+
+ switch ( paAttr->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ InterpMaskedData( CDmrArray< float >( paAttr ), CDmrArrayConst< float >( pbAttr ).Get(), weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ InterpMaskedData( CDmrArray< Vector4D >( paAttr ), CDmrArrayConst< Vector4D >( pbAttr ).Get(), weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ InterpMaskedData( CDmrArray< Vector2D >( paAttr ), CDmrArrayConst< Vector2D >( pbAttr ).Get(), weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ InterpMaskedData( CDmrArray< Vector >( paAttr ), CDmrArrayConst< Vector >( pbAttr ).Get(), weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Interpolates between the specified VertexData and the specified Delta
+// If pBase is NULL it will become the current state
+// If pDelta is NULL then the state to interpolate to will be the bind state
+//-----------------------------------------------------------------------------
+bool CDmeMesh::InterpMaskedDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeVertexData *pDst /* = NULL */,
+ float weight /*= 1.0f */,
+ const CDmeSingleIndexedComponent *pMask /*= NULL */ )
+{
+ CDmeVertexData *pDstBase = pDst ? pDst : GetCurrentBaseState();
+ CDmeVertexData *pBind = GetBindBaseState();
+
+ if ( !pDstBase || !pBind || pDstBase == pBind )
+ return false;
+
+ if ( pDelta == NULL )
+ {
+ // Interpolate between specified state and bind state
+ return InterpMaskedData( pDstBase, pBind, weight, pMask );
+ }
+
+ // This should be cached and recomputed only when states are added
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDependentDeltaStateList( compList );
+
+ bool retVal = false;
+
+ const int nDeltas( compList.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( pDelta != GetDeltaState( compList[ i ].m_nDeltaIndex ) )
+ continue;
+
+ retVal = true;
+
+ const int nBaseField( pDstBase->FieldCount() );
+ const int nBindField( pBind->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ CUtlVector< float > floatData;
+ CUtlVector< Vector2D > vector2DData;
+ CUtlVector< Vector > vectorData;
+ CUtlVector< Vector4D > vector4DData;
+
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pDstBase->FieldName( j ) );
+
+ for ( int k = 0; k < nBindField; ++k )
+ {
+ const CUtlString &bindFieldName( pBind->FieldName( k ) );
+ if ( baseFieldName != bindFieldName )
+ continue;
+
+ for ( int l = 0; l < nDeltaField; ++l )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( l ) );
+ if ( bindFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pDstBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t bindFieldIndex( pBind->FindFieldIndex( bindFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+
+ if ( baseFieldIndex < 0 || bindFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pDstBaseData( pDstBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pBindData( pBind->GetVertexData( bindFieldIndex ) );
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pDstBaseData->GetType() != pBindData->GetType() || pBindData->GetType() != pDeltaData->GetType() )
+ break;
+
+ const CUtlVector< int > &bindIndices( pBind->GetVertexIndexData( bindFieldIndex ) );
+
+ switch ( pDstBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ floatData = CDmrArrayConst< float >( pBindData ).Get();
+ AddCorrectedDelta( floatData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< float >( pDstBaseData ), floatData, weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ vector4DData = CDmrArrayConst< Vector4D >( pBindData ).Get();
+ AddCorrectedDelta( vector4DData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< Vector4D >( pDstBaseData ), vector4DData, weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ vector2DData = CDmrArrayConst< Vector2D >( pBindData ).Get();
+ AddCorrectedDelta( vector2DData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< Vector2D >( pDstBaseData ), vector2DData, weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ vectorData = CDmrArrayConst< Vector >( pBindData ).Get();
+ AddCorrectedDelta( vectorData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< Vector >( pDstBaseData ), vectorData, weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the index of the closest selected vertex in the mesh to vIndex
+// -1 on failure
+//-----------------------------------------------------------------------------
+int CDmeMesh::ClosestSelectedVertex( int vIndex, CDmeSingleIndexedComponent *pSelection, const CDmeVertexData *pPassedBase /* = NULL */ ) const
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return -1;
+
+ const CUtlVector< Vector > &positions( pBase->GetPositionData() );
+
+ if ( vIndex >= positions.Count() )
+ return -1;
+
+ const Vector &p( positions[ vIndex ] );
+
+ CUtlVector< int > verts;
+ pSelection->GetComponents( verts );
+ const int nVerts = verts.Count();
+
+ if ( nVerts <= 0 )
+ return -1;
+
+ float minSqDist = p.DistToSqr( positions[ verts[ 0 ] ] );
+ float tmpSqDist;
+
+ int retVal = verts[ 0 ];
+ for ( int i = 1; i < nVerts; ++i )
+ {
+ tmpSqDist = p.DistToSqr( positions[ verts[ i ] ] );
+ if ( tmpSqDist < minSqDist )
+ {
+ minSqDist = tmpSqDist;
+ retVal = verts[ i ];
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+float CDmeMesh::DistanceBetween( int vIndex0, int vIndex1, const CDmeVertexData *pPassedBase /*= NULL */ ) const
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return 0.0f;
+
+ const CUtlVector< Vector > &positions( pBase->GetPositionData() );
+ const int nPositions = positions.Count();
+
+ if ( vIndex0 >= nPositions || vIndex1 >= nPositions )
+ return 0.0f;
+
+ return positions[ vIndex0 ].DistTo( positions[ vIndex1 ] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sorts DeltaComputation_t's by dimensionality
+//-----------------------------------------------------------------------------
+int ControlIndexLessFunc( const void *lhs, const void *rhs )
+{
+ const int &lVal = *reinterpret_cast< const int * >( lhs );
+ const int &rVal = *reinterpret_cast< const int * >( rhs );
+ return lVal - rVal;
+}
+
+//-----------------------------------------------------------------------------
+// This will compute a list of delta states that are superior to the passed
+// delta state name (which has the form of <NAME>[_<NAME>]..., i.e. controls
+// separated by underscores. The states will be returned in order from
+// most superior to least superior. Since the deltas need to be broken down
+// by the control deltas, if any control delta doesn't exist it will return false.
+//
+// A superior delta state is defined as a delta which has this delta as
+// a dependent (or inferior) delta.
+//
+// Given the network of:
+//
+// A, B, C
+// A_B, A_C, B_C
+// A_B_C
+//
+// A_B_C is superior to A, B, A_B, A_C & B_C
+// A_B is superior to A, B & C
+// A_C is superior to A, B & C
+// B_C is superior to A, B & C
+//
+// Input Output
+// ------- --------------------
+// A A_B_C, A_B, A_C, B_C
+// B A_B_C, A_B, A_C, B_C
+// C A_B_C, A_B, A_C, B_C
+// A_B A_B_C
+// A_C A_B_C
+// B_C A_B_C
+// A_B_C
+//-----------------------------------------------------------------------------
+bool CDmeMesh::ComputeSuperiorDeltaStateList( const char *pInferiorDeltaName, CUtlVector< int > &superiorDeltaStates )
+{
+ // TODO: Compute this data only when the deltas are added, removed or renamed
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDeltaStateComputationList( compList );
+
+ // Typically the passed delta won't be in the list yet, but it could be, that's ok
+ // Treat it like it isn't to be sure.
+ CUtlVector< int > inferiorIndices;
+ if ( !GetControlDeltaIndices( pInferiorDeltaName, inferiorIndices ) )
+ return false;
+
+ const int nInferiorIndices = inferiorIndices.Count();
+ qsort( inferiorIndices.Base(), nInferiorIndices, sizeof( int ), ControlIndexLessFunc );
+
+ CUtlVector< int > superiorIndices;
+ int nSuperiorIndices;
+ CDmeVertexDeltaData *pSuperiorDelta;
+
+ for ( int i = compList.Count() - 1; i >= 0; --i )
+ {
+ const DeltaComputation_t &deltaComp = compList[ i ];
+
+ // For a delta to be superior, it has to have more control inputs than the specified delta
+ // compList is sorted in order of dimensionality, so safe to abort
+ if ( nInferiorIndices >= deltaComp.m_nDimensionality )
+ break;
+
+ pSuperiorDelta = GetDeltaState( deltaComp.m_nDeltaIndex );
+ if ( !pSuperiorDelta )
+ continue;
+
+ if ( !GetControlDeltaIndices( pSuperiorDelta, superiorIndices ) )
+ continue;
+
+ nSuperiorIndices = superiorIndices.Count();
+
+ qsort( superiorIndices.Base(), nSuperiorIndices, sizeof( int ), ControlIndexLessFunc );
+
+ int nFound = 0;
+ int si = 0;
+ for ( int ii = 0; ii < nInferiorIndices; ++ii )
+ {
+ const int &iIndex = inferiorIndices[ ii ];
+ while ( si < nSuperiorIndices && iIndex != superiorIndices[ si ] )
+ {
+ ++si;
+ }
+
+ if ( si < nSuperiorIndices )
+ {
+ ++nFound;
+ }
+ }
+
+ if ( nFound == nInferiorIndices )
+ {
+ superiorDeltaStates.AddToTail( deltaComp.m_nDeltaIndex );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes the passed base state from the list of base states in the mesh
+// if it exists in the list of base states in the mesh, but doesn't delete
+// the element itself
+//-----------------------------------------------------------------------------
+bool CDmeMesh::RemoveBaseState( CDmeVertexData *pBase )
+{
+ const int nBaseStates = m_BaseStates.Count();
+ for ( int i = 0; i < nBaseStates; ++i )
+ {
+ if ( m_BaseStates[ i ] == pBase )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an existing element to the list of base states of the mesh if it
+// isn't already one of the base states
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::FindOrAddBaseState( CDmeVertexData *pBase )
+{
+ const int nBaseStates = m_BaseStates.Count();
+ for ( int i = 0; i < nBaseStates; ++i )
+ {
+ if ( m_BaseStates[ i ] == pBase )
+ {
+ return pBase;
+ }
+ }
+
+ return m_BaseStates[ m_BaseStates.AddToTail( pBase ) ];
+}
+
+
+//-----------------------------------------------------------------------------
+// TODO: Current state is insufficient as long as the current state isn't
+// created from the current delta weights
+//-----------------------------------------------------------------------------
+void CDmeMesh::GetBoundingSphere(
+ Vector &c, float &r,
+ CDmeVertexData *pPassedBase /* = NULL */, CDmeSingleIndexedComponent *pPassedSelection /* = NULL */ ) const
+{
+ c.Zero();
+ r = 0.0f;
+
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return;
+
+ const FieldIndex_t pIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( pIndex < 0 )
+ return;
+
+ const CUtlVector< Vector > &pData( pBase->GetPositionData() );
+ const int nPositions = pData.Count();
+
+ if ( pPassedSelection )
+ {
+ const int nSelectionCount = pPassedSelection->Count();
+ int nIndex;
+ float fWeight;
+ for ( int i = 0; i < nSelectionCount; ++i )
+ {
+ pPassedSelection->GetComponent( i, nIndex, fWeight );
+ c += pData[ nIndex ];
+ }
+
+ c /= static_cast< float >( nSelectionCount );
+
+ float sqDist;
+ for ( int i = 0; i < nSelectionCount; ++i )
+ {
+ for ( int iPos = 0; iPos < nPositions; ++iPos )
+ {
+ sqDist = c.DistToSqr( pData[ iPos ] );
+ if ( sqDist > r )
+ {
+ r = sqDist;
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nPositions; ++i )
+ {
+ c += pData[ i ];
+ }
+
+ c /= static_cast< float >( nPositions );
+
+ float sqDist;
+ for ( int i = 0; i < nPositions; ++i )
+ {
+ for ( int iPos = 0; iPos < nPositions; ++iPos )
+ {
+ sqDist = c.DistToSqr( pData[iPos] );
+ if ( sqDist > r )
+ {
+ r = sqDist;
+ }
+ }
+ }
+ }
+
+ r = sqrt( r );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::GetBoundingBox( Vector &min, Vector &max, CDmeVertexData *pPassedBase /* = NULL */, CDmeSingleIndexedComponent *pPassedSelection /* = NULL */ ) const
+{
+ min.Zero();
+ max.Zero();
+
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return;
+
+ const FieldIndex_t pIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( pIndex < 0 )
+ return;
+
+ const CUtlVector< Vector > &pData( pBase->GetPositionData() );
+ const int nPositions = pData.Count();
+
+ if ( pPassedSelection )
+ {
+ const int nSelectionCount = pPassedSelection->Count();
+
+ if ( nSelectionCount > 0 )
+ {
+ int nIndex;
+ float fWeight;
+
+ pPassedSelection->GetComponent( 0, nIndex, fWeight );
+ min = pData[ nIndex ];
+ max = min;
+
+ for ( int i = 1; i < nSelectionCount; ++i )
+ {
+ pPassedSelection->GetComponent( i, nIndex, fWeight );
+
+ const Vector &p = pData[ nIndex ];
+ if ( p.x < min.x )
+ {
+ min.x = p.x;
+ }
+ else if ( p.x > max.x )
+ {
+ max.x = p.x;
+ }
+
+ if ( p.y < min.y )
+ {
+ min.y = p.y;
+ }
+ else if ( p.y > max.y )
+ {
+ max.y = p.y;
+ }
+
+ if ( p.z < min.z )
+ {
+ min.z = p.z;
+ }
+ else if ( p.z > max.z )
+ {
+ max.z = p.z;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( nPositions > 0 )
+ {
+ min = pData[ 0 ];
+ max = min;
+
+ for ( int i = 1; i < nPositions; ++i )
+ {
+ const Vector &p = pData[ i ];
+ if ( p.x < min.x )
+ {
+ min.x = p.x;
+ }
+ else if ( p.x > max.x )
+ {
+ max.x = p.x;
+ }
+
+ if ( p.y < min.y )
+ {
+ min.y = p.y;
+ }
+ else if ( p.y > max.y )
+ {
+ max.y = p.y;
+ }
+
+ if ( p.z < min.z )
+ {
+ min.z = p.z;
+ }
+ else if ( p.z > max.z )
+ {
+ max.z = p.z;
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+bool CDmeMesh::SetBaseDataToDeltas(
+ CDmeVertexData *pBase,
+ CDmeVertexData::StandardFields_t nStandardField, CDmrArrayConst< T_t > &srcData, CDmrArray< T_t > &dstData, bool bDoStereo, bool bDoLag )
+{
+ const int nDataCount = dstData.Count();
+ if ( srcData.Count() != nDataCount )
+ return false;
+
+ // Create the temp buffer for the data
+ T_t *pData = reinterpret_cast< T_t * >( alloca( nDataCount * sizeof( T_t ) ) );
+
+ // Copy the data from the src base state
+ memcpy( pData, srcData.Base(), nDataCount * sizeof( T_t ) );
+
+ const int nCount = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count();
+ Assert( m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count() == m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED].Count() );
+
+ if ( bDoStereo )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].y;
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].y;
+ if ( flLeftWeight <= 0.0f && flRightWeight <= 0.0f && ( !bDoLag || ( flLeftWeightLagged <= 0.0f && flRightWeightLagged <= 0.0f ) ) )
+ continue;
+
+ AddStereoVertexDelta< T_t >( pBase, pData, sizeof( T_t ), nStandardField, i, bDoLag );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ if ( flWeight < 0.0f && ( !bDoLag || flWeightLagged <= 0.0f ) )
+ continue;
+
+ AddVertexDelta< T_t >( pBase, pData, sizeof( T_t ), nStandardField, i, bDoLag );
+ }
+ }
+
+ dstData.SetMultiple( 0, nDataCount, pData );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the specified based state to the version of the mesh specified by the
+// current weighted deltas
+// It's ok to modify the bind state... if you know what you're doing
+//-----------------------------------------------------------------------------
+bool CDmeMesh::SetBaseStateToDeltas( CDmeVertexData *pPassedBase /*= NULL */ )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+
+ if ( !pBind || !pBase )
+ return false;
+
+ CDmeVertexData::StandardFields_t deltaFields[] =
+ {
+ CDmeVertexData::FIELD_POSITION,
+ CDmeVertexData::FIELD_NORMAL,
+ CDmeVertexData::FIELD_WRINKLE
+ };
+
+ const bool bDoStereo = ( pBind->FindFieldIndex( CDmeVertexDeltaData::FIELD_BALANCE ) >= 0 );
+
+ for ( int i = 0; i < sizeof( deltaFields ) / sizeof( deltaFields[ 0 ] ); ++i )
+ {
+ const CDmeVertexDeltaData::StandardFields_t nStandardField = deltaFields[ i ];
+ const int nSrcField = pBind->FindFieldIndex( nStandardField );
+ const int nDstField = pBase->FindFieldIndex( nStandardField );
+ if ( nSrcField < 0 || nDstField < 0 )
+ continue;
+
+ const CDmAttribute *pSrcAttr = pBind->GetVertexData( nSrcField );
+ CDmAttribute *pDstAttr = pBase->GetVertexData( nDstField );
+ if ( !pSrcAttr || !pDstAttr || pSrcAttr->GetType() != pDstAttr->GetType() )
+ continue;
+
+ switch ( pDstAttr->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ SetBaseDataToDeltas( pBind, nStandardField, CDmrArrayConst< float >( pSrcAttr ), CDmrArray< float >( pDstAttr ), bDoStereo, false );
+ break;
+ case AT_VECTOR3_ARRAY:
+ SetBaseDataToDeltas( pBind, nStandardField, CDmrArrayConst< Vector >( pSrcAttr ), CDmrArray< Vector >( pDstAttr ), bDoStereo, false );
+ break;
+ default:
+ Assert( 0 );
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Replace all instances of a material with a different material
+//-----------------------------------------------------------------------------
+void CDmeMesh::ReplaceMaterial( const char *pOldMaterialName, const char *pNewMaterialName )
+{
+ char pOldFixedName[MAX_PATH];
+ char pNewFixedName[MAX_PATH];
+ char pFixedName[MAX_PATH];
+ if ( pOldMaterialName )
+ {
+ V_FixupPathName( pOldFixedName, sizeof(pOldFixedName), pOldMaterialName );
+ }
+ V_FixupPathName( pNewFixedName, sizeof(pNewFixedName), pNewMaterialName );
+ V_FixSlashes( pNewFixedName, '/' );
+
+ CDmeMaterial *pReplacementMaterial = NULL;
+
+ int nCount = m_FaceSets.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = m_FaceSets[i];
+ CDmeMaterial *pMaterial = pFaceSet->GetMaterial();
+ if ( pOldMaterialName )
+ {
+ const char *pMaterialName = pMaterial->GetMaterialName();
+ V_FixupPathName( pFixedName, sizeof(pFixedName), pMaterialName );
+ if ( Q_stricmp( pFixedName, pOldFixedName ) )
+ continue;
+ }
+
+ if ( !pReplacementMaterial )
+ {
+ pReplacementMaterial = CreateElement< CDmeMaterial >( pMaterial->GetName(), pMaterial->GetFileId() );
+ pReplacementMaterial->SetMaterial( pNewFixedName );
+ }
+ pFaceSet->SetMaterial( pReplacementMaterial );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up delta data that is referring to normals which have been merged out
+//-----------------------------------------------------------------------------
+static void CollapseRedundantDeltaNormals( CDmeVertexDeltaData *pDmeDelta, const CUtlVector< int > &normalMap )
+{
+ if ( !pDmeDelta )
+ return;
+
+ FieldIndex_t nNormalFieldIndex = pDmeDelta->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nNormalFieldIndex < 0 )
+ return; // No normal deltas
+
+ const CUtlVector< Vector > &oldNormalData = pDmeDelta->GetNormalData();
+ const CUtlVector< int > &oldNormalIndices = pDmeDelta->GetVertexIndexData( nNormalFieldIndex );
+
+ Assert( oldNormalData.Count() == oldNormalIndices.Count() );
+
+ CUtlVector< bool > done;
+ done.SetCount( normalMap.Count() );
+ Q_memset( done.Base(), 0, done.Count() * sizeof( bool ) );
+
+ CUtlVector< Vector > newNormalData;
+ CUtlVector< int > newNormalIndices;
+
+ for ( int i = 0; i < oldNormalIndices.Count(); ++i )
+ {
+ const int nNewIndex = normalMap[ oldNormalIndices[i] ];
+ if ( nNewIndex < 0 || done[ nNewIndex ] )
+ continue;
+
+ done[ nNewIndex ] = true;
+ newNormalData.AddToTail( oldNormalData[i] );
+ newNormalIndices.AddToTail( nNewIndex );
+ }
+
+ pDmeDelta->RemoveAllVertexData( nNormalFieldIndex );
+ nNormalFieldIndex = pDmeDelta->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ pDmeDelta->AddVertexData( nNormalFieldIndex, newNormalData.Count() );
+ pDmeDelta->SetVertexData( nNormalFieldIndex, 0, newNormalData.Count(), AT_VECTOR3, newNormalData.Base() );
+ pDmeDelta->SetVertexIndices( nNormalFieldIndex, 0, newNormalIndices.Count(), newNormalIndices.Base() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove redundant normals from a DMX Mesh
+// Looks at all of the normals around each position vertex and merges normals
+// which are numerically similar (within flNormalBlend which by default in
+// studiomdl is within 2 degrees) around that vertex
+//
+// If this would result in more normals being created, then don't do anything
+// return false.
+//-----------------------------------------------------------------------------
+static bool CollapseRedundantBaseNormals( CDmeVertexData *pDmeVertexData, CUtlVector< int > &normalMap, float flNormalBlend )
+{
+ if ( !pDmeVertexData )
+ return false;
+
+ FieldIndex_t nPositionFieldIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t nNormalFieldIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nPositionFieldIndex < 0 || nNormalFieldIndex < 0 )
+ return false;
+
+ const CUtlVector< Vector > &oldNormalData = pDmeVertexData->GetNormalData();
+ const CUtlVector< int > &oldNormalIndices = pDmeVertexData->GetVertexIndexData( nNormalFieldIndex );
+
+ CUtlVector< Vector > newNormalData;
+ CUtlVector< int > newNormalIndices;
+
+ newNormalIndices.SetCount( oldNormalIndices.Count() );
+ for ( int i = 0; i < newNormalIndices.Count(); ++i )
+ {
+ newNormalIndices[i] = -1;
+ }
+
+ const int nPositionDataCount = pDmeVertexData->GetPositionData().Count();
+ for ( int i = 0; i < nPositionDataCount; ++i )
+ {
+ int nNewNormalDataIndex = newNormalData.Count();
+
+ const CUtlVector< int > &vertexIndices = pDmeVertexData->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, i );
+ for ( int j = 0; j < vertexIndices.Count(); ++j )
+ {
+ bool bUnique = true;
+ const int nVertexIndex = vertexIndices[j];
+ const Vector &vNormal = oldNormalData[ oldNormalIndices[ vertexIndices[j] ] ];
+
+ for ( int k = nNewNormalDataIndex; k < newNormalData.Count(); ++k )
+ {
+ if ( DotProduct( vNormal, newNormalData[k] ) > flNormalBlend )
+ {
+ newNormalIndices[ nVertexIndex ] = k;
+ bUnique = false;
+ break;
+ }
+ }
+
+ if ( !bUnique )
+ continue;
+
+ newNormalIndices[ nVertexIndex ] = newNormalData.AddToTail( vNormal );
+ }
+ }
+
+ for ( int i = 0; i < newNormalIndices.Count(); ++i )
+ {
+ if ( newNormalIndices[i] == -1 )
+ {
+ newNormalIndices[i] = newNormalData.AddToTail( oldNormalData[ oldNormalIndices[i] ] );
+ }
+ }
+
+ // If it's the same or more don't do anything
+ if ( newNormalData.Count() >= oldNormalData.Count() )
+ return false;
+
+ normalMap.SetCount( oldNormalData.Count() );
+ for ( int i = 0; i < normalMap.Count(); ++i )
+ {
+ normalMap[i] = -1;
+ }
+
+ Assert( newNormalIndices.Count() == oldNormalIndices.Count() );
+ for ( int i = 0; i < oldNormalIndices.Count(); ++i )
+ {
+ if ( normalMap[ oldNormalIndices[i] ] == -1 )
+ {
+ normalMap[ oldNormalIndices[i] ] = newNormalIndices[i];
+ }
+ else
+ {
+ Assert( normalMap[ oldNormalIndices[i] ] == newNormalIndices[i] );
+ }
+ }
+
+ pDmeVertexData->RemoveAllVertexData( nNormalFieldIndex );
+ nNormalFieldIndex = pDmeVertexData->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ pDmeVertexData->AddVertexData( nNormalFieldIndex, newNormalData.Count() );
+ pDmeVertexData->SetVertexData( nNormalFieldIndex, 0, newNormalData.Count(), AT_VECTOR3, newNormalData.Base() );
+ pDmeVertexData->SetVertexIndices( nNormalFieldIndex, 0, newNormalIndices.Count(), newNormalIndices.Base() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Collapse all normals with the same numerical value into the same normal
+//-----------------------------------------------------------------------------
+static bool CollapseRedundantBaseNormalsAggressive( CDmeVertexData *pDmeVertexData, float flNormalBlend )
+{
+ if ( !pDmeVertexData )
+ return false;
+
+ FieldIndex_t nNormalFieldIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nNormalFieldIndex < 0 )
+ return false;
+
+ const CUtlVector< Vector > &oldNormalData = pDmeVertexData->GetNormalData();
+ const CUtlVector< int > &oldNormalIndices = pDmeVertexData->GetVertexIndexData( nNormalFieldIndex );
+
+ CUtlVector< int > normalMap;
+ normalMap.SetCount( oldNormalData.Count() );
+
+ CUtlVector< Vector > newNormalData;
+
+ for ( int i = 0; i < oldNormalData.Count(); ++i )
+ {
+ bool bUnique = true;
+ const Vector &vNormal = oldNormalData[ i ];
+
+ for ( int j = 0; j < newNormalData.Count(); ++j )
+ {
+ if ( DotProduct( vNormal, newNormalData[j] ) > flNormalBlend )
+ {
+ normalMap[ i ] = j;
+ bUnique = false;
+ break;
+ }
+ }
+
+ if ( !bUnique )
+ continue;
+
+ normalMap[ i ] = newNormalData.AddToTail( vNormal );
+ }
+
+ // If it's the same then don't do anything.
+ if ( newNormalData.Count() >= oldNormalData.Count() )
+ return false;
+
+ CUtlVector< int > newNormalIndices;
+ newNormalIndices.SetCount( oldNormalIndices.Count() );
+
+ for ( int i = 0; i < oldNormalIndices.Count(); ++i )
+ {
+ newNormalIndices[i] = normalMap[ oldNormalIndices[i] ];
+ }
+
+ pDmeVertexData->RemoveAllVertexData( nNormalFieldIndex );
+ nNormalFieldIndex = pDmeVertexData->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ pDmeVertexData->AddVertexData( nNormalFieldIndex, newNormalData.Count() );
+ pDmeVertexData->SetVertexData( nNormalFieldIndex, 0, newNormalData.Count(), AT_VECTOR3, newNormalData.Base() );
+ pDmeVertexData->SetVertexIndices( nNormalFieldIndex, 0, newNormalIndices.Count(), newNormalIndices.Base() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::NormalizeNormals()
+{
+ Vector vNormal;
+
+ for ( int i = 0; i < this->BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pDmeVertexData = GetBaseState( i );
+ if ( !pDmeVertexData )
+ continue;
+
+ FieldIndex_t nNormalIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nNormalIndex < 0 )
+ continue;
+
+ CDmAttribute *pDmNormalAttr = pDmeVertexData->GetVertexData( nNormalIndex );
+ if ( !pDmNormalAttr )
+ continue;
+
+ CDmrArray< Vector > normalData( pDmNormalAttr );
+ for ( int j = 0; j < normalData.Count(); ++j )
+ {
+ vNormal = normalData.Get( j );
+ VectorNormalize( vNormal );
+ normalData.Set( j, vNormal );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::CollapseRedundantNormals( float flNormalBlend )
+{
+ NormalizeNormals();
+
+ CDmeVertexData *pDmeBind = GetBindBaseState();
+ if ( !pDmeBind )
+ return;
+
+ CUtlVector< int > normalMap;
+
+ const int nDeltaStateCount = DeltaStateCount();
+ if ( nDeltaStateCount <= 0 )
+ {
+ // No deltas
+ if ( CollapseRedundantBaseNormalsAggressive( pDmeBind, flNormalBlend ) )
+ {
+ // Collapse any other states
+ for ( int i = 0; i < BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pDmeVertexData = GetBaseState( i );
+ if ( !pDmeVertexData || pDmeVertexData == pDmeBind )
+ continue;
+
+ CollapseRedundantBaseNormalsAggressive( pDmeVertexData, flNormalBlend );
+ }
+ }
+ }
+ else
+ {
+ // Collapse the base state
+ if ( CollapseRedundantBaseNormals( pDmeBind, normalMap, flNormalBlend ) )
+ {
+ // Collapse any delta states using the baseState normal map
+ for ( int i = 0; i < DeltaStateCount(); ++i )
+ {
+ CDmeVertexDeltaData *pDmeDeltaData = GetDeltaState( i );
+ if ( !pDmeDeltaData )
+ continue;
+
+ CollapseRedundantDeltaNormals( pDmeDeltaData, normalMap );
+ }
+
+ // Collapse any other states
+ for ( int i = 0; i < BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pDmeVertexData = GetBaseState( i );
+ if ( !pDmeVertexData || pDmeVertexData == pDmeBind )
+ continue;
+
+ CollapseRedundantBaseNormals( pDmeVertexData, normalMap, flNormalBlend );
+ }
+ }
+ }
+} \ No newline at end of file