diff options
Diffstat (limited to 'movieobjects/dmmeshutils.cpp')
| -rw-r--r-- | movieobjects/dmmeshutils.cpp | 3313 |
1 files changed, 3313 insertions, 0 deletions
diff --git a/movieobjects/dmmeshutils.cpp b/movieobjects/dmmeshutils.cpp new file mode 100644 index 0000000..9492085 --- /dev/null +++ b/movieobjects/dmmeshutils.cpp @@ -0,0 +1,3313 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Functions which do things to a DmeMesh +// +//============================================================================= + + +// Valve includes +#include "movieobjects/dmeanimationset.h" +#include "movieobjects/dmecombinationoperator.h" +#include "movieobjects/dmemodel.h" +#include "movieobjects/dmedag.h" +#include "movieobjects/dmemesh.h" +#include "movieobjects/dmefaceset.h" +#include "movieobjects/dmematerial.h" +#include "movieobjects/dmevertexdata.h" +#include "movieobjects/dmmeshcomp.h" // TODO: This has to be included before dmmeshutils.h +#include "movieobjects/dmmeshutils.h" +#include "tier1/utlstack.h" +#include "tier2/p4helpers.h" +#include "tier1/utlstring.h" +#include "tier1/utlstringmap.h" +#include "tier1/utlbuffer.h" +#include "tier1/fmtstr.h" +#include "filesystem.h" + + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::RemoveLargeAxisAlignedPlanarFaces( CDmeMesh *pMesh ) +{ + CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) ); + if ( !pBase ) + return false; + + const int posIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + if ( posIndex < 0 ) + return false; + + const CUtlVector< Vector > &posData( CDmrArrayConst< Vector >( pBase->GetVertexData( posIndex ) ).Get() ); + if ( posData.Count() <= 0 ) + return false; + + const CUtlVector< int > &posIndices( CDmrArrayConst< int >( pBase->GetIndexData( posIndex ) ).Get() ); + if ( posIndices.Count() <= 0 ) + return false; + + bool bMeshChanged = false; + + CUtlVector< int > emptyFaceSets; + + int faceStartIndex = 0; + int faceCurrentIndex = 0; + + int faceVertexCount = 0; + + bool bPlanarX = true; + bool bPlanarY = true; + bool bPlanarZ = true; + + Vector p; + + CUtlVector< int > removeStart; + CUtlVector< int > removeCount; + + const int nFaceSets = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSets; ++i ) + { + CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + const int nFaceIndices = pFaceSet->NumIndices(); + if ( nFaceIndices <= 0 ) + continue; + + faceStartIndex = 0; + + faceCurrentIndex = pFaceSet->GetIndex( 0 ); + if ( faceCurrentIndex < 0 ) + continue; + + faceVertexCount = 0; + + bPlanarX = true; + bPlanarY = true; + bPlanarZ = true; + + removeStart.RemoveAll(); + removeCount.RemoveAll(); + + p = posData[ posIndices[ faceCurrentIndex ] ]; + + for ( int j = 1; j < nFaceIndices; ++j ) + { + faceCurrentIndex = pFaceSet->GetIndex( j ); + + if ( faceCurrentIndex < 0 ) + { + // End of a face + + if ( faceVertexCount > 4 && ( bPlanarX || bPlanarY || bPlanarZ ) ) + { + removeStart.AddToTail( faceStartIndex ); + removeCount.AddToTail( j - faceStartIndex + 1 ); + } + + faceStartIndex = j + 1; + + if ( faceStartIndex < nFaceIndices ) + { + p = posData[ posIndices[ pFaceSet->GetIndex( faceStartIndex ) ] ]; + } + + faceVertexCount = 0; + + bPlanarX = true; + bPlanarY = true; + bPlanarZ = true; + + continue; + } + + Assert( faceCurrentIndex < posIndices.Count() ); + Assert( posIndices[ faceCurrentIndex ] < posData.Count() ); + const Vector &vPos = posData[ posIndices[ faceCurrentIndex ] ]; + + if ( vPos.x != p.x ) + bPlanarX = false; + + if ( vPos.y != p.y ) + bPlanarY = false; + + if ( vPos.z != p.z ) + bPlanarZ = false; + + ++faceVertexCount; + } + + Assert( removeStart.Count() == removeCount.Count() ); + for ( int j = removeStart.Count() - 1; j >= 0; --j ) + { + pFaceSet->RemoveMultiple( removeStart[ j ], removeCount[ j ] ); + bMeshChanged = true; + } + + if ( pFaceSet->GetIndexCount() == 0 ) + { + emptyFaceSets.AddToTail( i ); + } + } + + for ( int i = emptyFaceSets.Count() - 1; i >= 0; --i ) + { + pMesh->RemoveFaceSet( emptyFaceSets[ i ] ); + bMeshChanged = true; + } + + if ( bMeshChanged ) + { + PurgeUnusedData( pMesh ); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::RemoveFacesWithMaterial( CDmeMesh *pMesh, const char *pMaterialName ) +{ + bool bMeshChanged = false; + + CUtlVector< int > emptyFaceSets; + + const int nFaceSets = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSets; ++i ) + { + CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + if ( !Q_strcmp( pFaceSet->GetMaterial()->GetMaterialName(), pMaterialName ) ) + { + emptyFaceSets.AddToTail( i ); + bMeshChanged = true; + } + } + + for ( int i = emptyFaceSets.Count() - 1; i >= 0; --i ) + { + pMesh->RemoveFaceSet( emptyFaceSets[ i ] ); + bMeshChanged = true; + } + + if ( bMeshChanged ) + { + PurgeUnusedData( pMesh ); + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::RemoveFacesWithMoreThanNVerts( CDmeMesh *pMesh, const int nVertexCount ) +{ + CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) ); + if ( !pBase ) + return false; + + bool bMeshChanged = false; + + CUtlVector< int > emptyFaceSets; + + int faceStartIndex = 0; + int faceCurrentIndex = 0; + + int faceVertexCount = 0; + + CUtlVector< int > removeStart; + CUtlVector< int > removeCount; + + const int nFaceSets = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSets; ++i ) + { + CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + const int nFaceIndices = pFaceSet->NumIndices(); + if ( nFaceIndices <= 0 ) + continue; + + faceStartIndex = 0; + + faceCurrentIndex = pFaceSet->GetIndex( 0 ); + if ( faceCurrentIndex < 0 ) + continue; + + faceVertexCount = 0; + + removeStart.RemoveAll(); + removeCount.RemoveAll(); + + for ( int j = 1; j < nFaceIndices; ++j ) + { + faceCurrentIndex = pFaceSet->GetIndex( j ); + + if ( faceCurrentIndex < 0 ) + { + // End of a face + + if ( faceVertexCount > nVertexCount ) + { + removeStart.AddToTail( faceStartIndex ); + removeCount.AddToTail( j - faceStartIndex + 1 ); + } + + faceStartIndex = j + 1; + + faceVertexCount = 0; + + continue; + } + + ++faceVertexCount; + } + + Assert( removeStart.Count() == removeCount.Count() ); + for ( int j = removeStart.Count() - 1; j >= 0; --j ) + { + pFaceSet->RemoveMultiple( removeStart[ j ], removeCount[ j ] ); + bMeshChanged = true; + } + + if ( pFaceSet->GetIndexCount() == 0 ) + { + emptyFaceSets.AddToTail( i ); + } + } + + for ( int i = emptyFaceSets.Count() - 1; i >= 0; --i ) + { + pMesh->RemoveFaceSet( emptyFaceSets[ i ] ); + bMeshChanged = true; + } + + if ( bMeshChanged ) + { + PurgeUnusedData( pMesh ); + return true; + } + + // Nothing remove + return false; +} + + +//----------------------------------------------------------------------------- +// Figures out which vertexIndices are missing +// Returned list will be in sorted order +//----------------------------------------------------------------------------- +void ComputeVertexIndexMap( CDmeMesh *pMesh, int nMaxVertexCount, CUtlVector< int > &vertexIndexMap ) +{ + bool *pVertexFound = reinterpret_cast< bool * >( alloca( nMaxVertexCount * sizeof( bool ) ) ); + memset( pVertexFound, 0, nMaxVertexCount * sizeof( bool ) ); + + // Loop through all the face sets to find out the highest vertex index + const int nFaceSetCount = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSetCount; ++i ) + { + const CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + const int nFaceSetIndices = pFaceSet->NumIndices(); + for ( int j = 0; j < nFaceSetIndices; ++j ) + { + const int &nIndex = pFaceSet->GetIndex( j ); + if ( nIndex >= 0 ) + { + Assert( nIndex < nMaxVertexCount ); + pVertexFound[ nIndex ] = true; + } + } + } + + int nMissingCount = 0; + for ( int i = 0; i < nMaxVertexCount; ++i ) + { + if ( !pVertexFound[ i ] ) + { + ++nMissingCount; + } + } + + vertexIndexMap.SetSize( nMaxVertexCount ); + for ( int i = 0; i < nMaxVertexCount; ++i ) + { + vertexIndexMap[ i ] = i; + } + + for ( int i = nMaxVertexCount - 1; i >= 0; --i ) + { + if ( !pVertexFound[ i ] ) + { + vertexIndexMap.Remove( i ); + } + } + + // Build up the reverse map + int *pReverseVertexIndexMap = reinterpret_cast< int * >( alloca( nMaxVertexCount * sizeof( int ) ) ); + for ( int i = 0; i < nFaceSetCount; ++i ) + { + pReverseVertexIndexMap[ i ] = -1; + } + + for ( int i = vertexIndexMap.Count() - 1; i >= 0; --i ) + { + pReverseVertexIndexMap[ vertexIndexMap[ i ] ] = i; + } + + // Fix up the face set indices to compensate for the ones which are going to be removed + for ( int i = 0; i < nFaceSetCount; ++i ) + { + CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + const int nFaceSetIndices = pFaceSet->NumIndices(); + for ( int j = 0; j < nFaceSetIndices; ++j ) + { + const int &nIndex = pFaceSet->GetIndex( j ); + if ( nIndex >= 0 ) + { + Assert( pReverseVertexIndexMap[ nIndex ] >= 0 ); + pFaceSet->SetIndex( j, pReverseVertexIndexMap[ nIndex ] ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Returns the highest vertex index used by the face sets of the mesh + 1 +//----------------------------------------------------------------------------- +int GetMaxVertexCount( const CDmeMesh *pMesh ) +{ + int nMaxVertexIndex = 0; + + // Loop through all the face sets to find out the highest vertex index + const int nFaceSetCount = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSetCount; ++i ) + { + const CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + const int nFaceSetIndices = pFaceSet->NumIndices(); + for ( int j = 0; j < nFaceSetIndices; ++j ) + { + const int &nIndex = pFaceSet->GetIndex( j ); + + if ( nIndex > nMaxVertexIndex ) + { + nMaxVertexIndex = nIndex; + } + } + } + + return nMaxVertexIndex + 1; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template < class T_t > +void RemapData( + CDmrArray< T_t > data, + const CUtlVector< int > &newToOldMap ) +{ + const int nNewToOldMapCount = newToOldMap.Count(); + + T_t *pNewData = reinterpret_cast< T_t * >( alloca( nNewToOldMapCount * sizeof( T_t ) ) ); + for ( int i = 0; i < nNewToOldMapCount; ++i ) + { + pNewData[ i ] = data.Get( newToOldMap[ i ] ); + } + + data.RemoveMultiple( nNewToOldMapCount, data.Count() - nNewToOldMapCount ); + data.SetMultiple( 0, nNewToOldMapCount, pNewData ); +} + + +//----------------------------------------------------------------------------- +// Computes the map of new data indices to old data indices +//----------------------------------------------------------------------------- +void RemoveUnusedData( + CDmeMesh *pMesh, + CDmeVertexData *pVertexData, + bool bBind, + const char *pFieldName, + int *pIndices, + int nIndicesCount, + CDmrGenericArray &data ) +{ + const int nDataCount = data.Count(); + + bool *pDataIndexFound = reinterpret_cast< bool * >( alloca( nDataCount * sizeof( bool ) ) ); + memset( pDataIndexFound, 0, nDataCount * sizeof( bool ) ); + + // Figure out which data is used + for ( int i = 0; i < nIndicesCount; ++i ) + { + Assert( pIndices[ i ] >= 0 && pIndices[ i ] < nDataCount ); + pDataIndexFound[ pIndices[ i ] ] = true; + } + + int nMissingCount = 0; + for ( int i = 0; i < nDataCount; ++i ) + { + if ( !pDataIndexFound[ i ] ) + { + ++nMissingCount; + } + } + + // Compute the New to Old data map + CUtlVector< int > newToOldDataMap; + newToOldDataMap.SetSize( nDataCount ); + for ( int i = 0; i < nDataCount; ++i ) + { + newToOldDataMap[ i ] = i; + } + + for ( int i = nDataCount - 1; i >= 0; --i ) + { + if ( !pDataIndexFound[ i ] ) + { + newToOldDataMap.Remove( i ); + } + } + + // Fix up the data + CDmAttribute *pDataAttr = data.GetAttribute(); + const DmAttributeType_t dataAttrType = pDataAttr->GetType(); + switch ( dataAttrType ) + { + case AT_FLOAT_ARRAY: + RemapData( CDmrArray< float >( pDataAttr ), newToOldDataMap ); + break; + case AT_VECTOR2_ARRAY: + RemapData( CDmrArray< Vector2D >( pDataAttr ), newToOldDataMap ); + break; + case AT_VECTOR3_ARRAY: + RemapData( CDmrArray< Vector >( pDataAttr ), newToOldDataMap ); + break; + case AT_VECTOR4_ARRAY: + RemapData( CDmrArray< Vector4D >( pDataAttr ), newToOldDataMap ); + break; + case AT_QUATERNION_ARRAY: + RemapData( CDmrArray< Quaternion >( pDataAttr ), newToOldDataMap ); + break; + case AT_COLOR_ARRAY: + RemapData( CDmrArray< Color >( pDataAttr ), newToOldDataMap ); + break; + default: + Assert( 0 ); + break; + } + + // Compute Old To New Data Map + int *pOldToNewDataMap = reinterpret_cast< int * >( alloca( nDataCount * sizeof( int ) ) ); + for ( int i = 0; i < nDataCount; ++i ) + { + pOldToNewDataMap[ i ] = -1; + } + + for ( int i = newToOldDataMap.Count() - 1; i >= 0; --i ) + { + pOldToNewDataMap[ newToOldDataMap[ i ] ] = i; + } + + // Fix up the indices + for ( int i = 0; i < nIndicesCount; ++i ) + { + pIndices[ i ] = pOldToNewDataMap[ pIndices[ i ] ]; + } + + // TODO: Fix up "jointWeight & "jointIndices" if this is "position" + if ( !Q_strcmp( pFieldName, "position" ) ) + { + const int nFields = pVertexData->FieldCount(); + for ( int i = 0; i < nFields; ++i ) + { + + } + } + + // If this is the bind state then fix up any delta states + if ( !bBind ) + return; + + // Fix up any Delta states + const int nDeltaStateCount = pMesh->DeltaStateCount(); + for ( int i = 0; i < nDeltaStateCount; ++i ) + { + CDmeVertexDeltaData *pDelta = pMesh->GetDeltaState( i ); + const int nDeltaFieldCount = pDelta->FieldCount(); + for ( int j = 0; j < nDeltaFieldCount; ++j ) + { + if ( !Q_strcmp( pFieldName, pDelta->FieldName( j ) ) ) + { + CDmrArray< int > deltaIndices = pDelta->GetIndexData( j ); + CDmrGenericArray deltaData = pDelta->GetVertexData( j ); + Assert( deltaIndices.Count() == deltaData.Count() ); + + for ( int k = deltaIndices.Count() - 1; k >= 0; --k ) + { + const int oldIndex = deltaIndices.Get( k ); + const int &newIndex = pOldToNewDataMap[ oldIndex ]; + if ( newIndex < 0 ) + { + deltaIndices.Remove( k ); + deltaData.Remove( k ); + } + else if ( newIndex != oldIndex ) + { + deltaIndices.Set( k, newIndex ); + } + } + } + } + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void RemoveUnusedVerticesFromBaseState( + CDmeMesh *pMesh, + CDmeVertexData *pVertexData, + const CUtlVector< int > &newToOldIndexMap ) +{ + const int nNewToOldIndexMapCount = newToOldIndexMap.Count(); + int *pNewVertexIndices = reinterpret_cast< int * >( alloca( nNewToOldIndexMapCount * sizeof( int ) ) ); + + // See if this is the bind state for the mesh + const bool bBind = !Q_strcmp( pVertexData->GetName(), "bind" ); + + const int nFieldCount = pVertexData->FieldCount(); + for ( int i = 0; i < nFieldCount; ++i ) + { + const char *pFieldName = pVertexData->FieldName( i ); + // TODO: Checking by name is lame... should be a lookup to map fieldIndex to a standard field index + if ( !Q_strcmp( pFieldName, "jointWeights" ) || !Q_strcmp( pFieldName, "jointIndices" ) ) + { + // TODO: Handle when positions are Remapped + continue; + } + + CDmrArray< int > indices = pVertexData->GetIndexData( i ); + + // Create the new index array accounting for missing indices + for ( int j = 0; j < nNewToOldIndexMapCount; ++j ) + { + Assert( newToOldIndexMap[ j ] < indices.Count() ); + pNewVertexIndices[ j ] = indices.Get( newToOldIndexMap[ j ] ); + } + + CDmrGenericArray data = pVertexData->GetVertexData( i ); + + // This will also update pNewVertexIndices + RemoveUnusedData( pMesh, pVertexData, bBind, pFieldName, pNewVertexIndices, nNewToOldIndexMapCount, CDmrGenericArray( pVertexData->GetVertexData( i ) ) ); + + // Shrink the indices array + indices.RemoveMultiple( nNewToOldIndexMapCount, indices.Count() - nNewToOldIndexMapCount ); + + // Set the new index values + indices.SetMultiple( 0, nNewToOldIndexMapCount, pNewVertexIndices ); + } + + // Update the vertex count + pVertexData->Resolve(); +} + + +//----------------------------------------------------------------------------- +// Removes unused data from the mesh +// Unused means a 'vertex' that isn't referred to by any face +// Once all unused vertices are removed, unused data is removed from each +// bit of data +// TODO: Also loop through each field of data, see which ones are no longer +// being referred to and then purge the data as well +// Would also have to purge delta data at the same time +// Would also have to purge joints at the same time (for position) +//----------------------------------------------------------------------------- +bool CDmMeshUtils::PurgeUnusedData( CDmeMesh *pMesh ) +{ + // Get the maximum vertex index of the mesh + const int nMaxVertexCount = GetMaxVertexCount( pMesh ); + + // Now find any missing indices + CUtlVector< int > vertexIndexMap; + ComputeVertexIndexMap( pMesh, nMaxVertexCount, vertexIndexMap ); + + // Remove the redundant vertices from all base states + for ( int i = pMesh->BaseStateCount() - 1; i >= 0; --i ) + { + RemoveUnusedVerticesFromBaseState( pMesh, pMesh->GetBaseState( i ), vertexIndexMap ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::Mirror( CDmeMesh *pMesh, int axis /*= kXAxis */ ) +{ + CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) ); + if ( !pBase ) + return false; + + CUtlVector< int > mirrorMap; + if ( !MirrorVertices( pMesh, pBase, axis, mirrorMap ) ) + return false; + + int vertexIndex; + int faceStart = 0; + + CUtlVector< int > newFaceIndices; + + const int nFaceSets = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSets; ++i ) + { + CDmeFaceSet *pSrcFaceSet = pMesh->GetFaceSet( i ); + const int nFaceSetIndices = pSrcFaceSet->NumIndices(); + if ( nFaceSetIndices <= 0 ) + continue; + + CDmeFaceSet *pDstFaceSet = pSrcFaceSet; + + // See if a new face set needs to be created + + CDmeMaterial *pSrcMaterial = pSrcFaceSet->GetMaterial(); + const char *pSrcMaterialName = pSrcMaterial->GetMaterialName(); + const int nNameLen = Q_strlen( pSrcMaterialName ); + if ( nNameLen >= 2 ) + { + CUtlString materialName; + + if ( !Q_stricmp( pSrcMaterialName + nNameLen - 2, "_l" ) ) + { + materialName = pSrcMaterialName; + materialName.SetLength( nNameLen - 2 ); + materialName += "_r"; + } + else if ( !Q_stricmp( pSrcMaterialName + nNameLen - 2, "_r" ) ) + { + materialName = pSrcMaterialName; + materialName.SetLength( nNameLen - 2 ); + materialName += "_l"; + } + else if ( nNameLen >= 5 && !Q_stricmp( pSrcMaterialName + nNameLen - 5, "_left" ) ) + { + materialName = pSrcMaterialName; + materialName.SetLength( nNameLen - 5 ); + materialName += "_right"; + } + else if ( nNameLen >= 6 && !Q_stricmp( pSrcMaterialName + nNameLen - 6, "_right" ) ) + { + materialName = pSrcMaterialName; + materialName.SetLength( nNameLen - 6 ); + materialName += "_left"; + } + + if ( materialName.Length() ) + { + pDstFaceSet = CreateElement< CDmeFaceSet >( materialName, pMesh->GetFileId() ); + CDmeMaterial *pDstMaterial = CreateElement< CDmeMaterial >( materialName, pDstFaceSet->GetFileId() ); + pDstMaterial->SetMaterial( materialName ); + pDstFaceSet->SetMaterial( pDstMaterial ); + pMesh->AddFaceSet( pDstFaceSet ); + } + } + + faceStart = 0; + + for ( int j = 0; j < nFaceSetIndices; ++j ) + { + vertexIndex = pSrcFaceSet->GetIndex( j ); + + if ( vertexIndex < 0 ) + { + newFaceIndices.RemoveAll(); + + for ( int k = j - 1; k >= faceStart; --k ) + { + newFaceIndices.AddToTail( mirrorMap[ pSrcFaceSet->GetIndex( k ) ] ); + } + newFaceIndices.AddToTail( -1 ); + + const int oldNumIndices = pDstFaceSet->NumIndices(); + + pDstFaceSet->AddIndices( newFaceIndices.Count() ); + pDstFaceSet->SetIndices( oldNumIndices, newFaceIndices.Count(), newFaceIndices.Base() ); + + // End of face + faceStart = j + 1; + continue; + } + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Initializes the CUtlVector to a linear ramp where utlVector[ i ] == i +//----------------------------------------------------------------------------- +template < typename T_t > +void RampInit( CUtlVector< T_t > &utlVector, const int nCount ) +{ + utlVector.SetCount( nCount ); + for ( int i = 0; i < nCount; ++i ) + { + utlVector[ i ] = i; + } +} + + +//----------------------------------------------------------------------------- +// Build Data Mirror Map +// Returns a pointer to the memory holding the indices for the map or NULL +//----------------------------------------------------------------------------- +const int *CDmMeshUtils::BuildDataMirrorMap( CDmeVertexData *pBase, int axis, CDmeVertexData::StandardFields_t standardField, CUtlVector< int > &dataMirrorMap ) +{ + const FieldIndex_t fieldIndex = pBase->FindFieldIndex( standardField ); + if ( fieldIndex < 0 ) + return NULL; + + const CUtlVector< int > &indices( CDmrArrayConst< int >( pBase->GetIndexData( fieldIndex ) ).Get() ); + + CDmAttribute *pData = pBase->GetVertexData( fieldIndex ); + if ( standardField == CDmeVertexData::FIELD_POSITION || standardField == CDmeVertexData::FIELD_NORMAL ) + { + const Vector mirrorOrigin( 0.0f, 0.0f, 0.0f ); + const float mirrorAxisVal = mirrorOrigin[ axis ]; + + CDmrArray< Vector > data( pBase->GetVertexData( fieldIndex ) ); + Vector v; + + const int nDataCount = data.Count(); + dataMirrorMap.SetCount( nDataCount ); + + int nMirrorDataCount = nDataCount; + for ( int i = 0; i < nDataCount; ++i ) + { + if ( fabs( data[ i ][ axis ] - mirrorAxisVal ) > FLT_EPSILON * 1000.0f ) + { + dataMirrorMap[ i ] = nMirrorDataCount++; + } + else + { + dataMirrorMap[ i ] = i; + v = data[ i ]; + v[ axis ] = mirrorOrigin[ axis ]; + data.Set( i, v ); + } + } + } + else if ( standardField == CDmeVertexData::FIELD_TEXCOORD ) + { + const Vector2D mirrorOrigin( 0.5f, 0.5f ); + const float mirrorAxisVal = mirrorOrigin[ axis % 2 ]; + + const CUtlVector< Vector2D > &data( CDmrArrayConst< Vector2D >( pBase->GetVertexData( fieldIndex ) ).Get() ); + const int nDataCount = data.Count(); + dataMirrorMap.SetCount( nDataCount ); + + int nMirrorDataCount = nDataCount; + for ( int i = 0; i < nDataCount; ++i ) + { + if ( fabs( data[ i ][ axis ] - mirrorAxisVal ) > FLT_EPSILON * 1000.0f ) + { + dataMirrorMap[ i ] = nMirrorDataCount++; + } + else + { + dataMirrorMap[ i ] = i; + } + } + } + else + { + RampInit( dataMirrorMap, CDmrGenericArrayConst( pData ).Count() ); + } + + return indices.Base(); +} + + +//----------------------------------------------------------------------------- +// y = mirrorMap[ x ] means that if y < 0 then original position x is not +// mirrored. Otherwise y is the index into the vertex indices of the mirrored +// version of vertex +//----------------------------------------------------------------------------- +bool CDmMeshUtils::MirrorVertices( CDmeMesh *pMesh, CDmeVertexData *pBase, int axis, CUtlVector< int > &mirrorMap ) +{ + mirrorMap.RemoveAll(); + + if ( !pMesh || !pBase || axis < kXAxis || axis > kZAxis ) + return false; + + const int posIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + if ( posIndex < 0 ) + return false; + + const CUtlVector< int > &posIndices( CDmrArrayConst< int >( pBase->GetIndexData( posIndex ) ).Get() ); + const int nIndices = posIndices.Count(); + Assert( nIndices == pBase->VertexCount() ); + CUtlVector< int > posMirrorMap; + + if ( !BuildDataMirrorMap( pBase, axis, CDmeVertexData::FIELD_POSITION, posMirrorMap ) ) + return false; + + CUtlVector< int > normalMirrorMap; + const int *pNormalIndices = BuildDataMirrorMap( pBase, axis, CDmeVertexData::FIELD_NORMAL, normalMirrorMap ); + + CUtlVector< int > uvMirrorMap; + const int *pUVIndices = BuildDataMirrorMap( pBase, axis, CDmeVertexData::FIELD_TEXCOORD, uvMirrorMap ); + + RampInit( mirrorMap, nIndices ); + int mirrorCount = 0; + { + bool mirror; + + Vector tmpVec; + Vector2D tmpVec2D; + + for ( int i = 0; i < nIndices; ++i ) + { + mirror = false; + + if ( posMirrorMap[ posIndices[ i ] ] != posIndices[ i ] ) + { + mirror = true; + } + + if ( pNormalIndices && normalMirrorMap[ pNormalIndices[ i ] ] != pNormalIndices[ i ] ) + { + mirror = true; + } + + if ( pUVIndices && uvMirrorMap[ pUVIndices[ i ] ] != pUVIndices[ i ] ) + { + mirror = true; + } + + if ( mirror ) + { + mirrorMap[ i ] = nIndices + mirrorCount; + ++mirrorCount; + } + } + } + + const int nBaseState = pMesh->BaseStateCount(); + for ( int i = 0; i < nBaseState; ++i ) + { + pBase = pMesh->GetBaseState( i ); + const int nVertexCount = pBase->VertexCount(); + MirrorVertices( pBase, axis, nVertexCount, mirrorCount, mirrorMap, posMirrorMap, normalMirrorMap, uvMirrorMap ); + } + + const int nDeltaState = pMesh->DeltaStateCount(); + for ( int i = 0; i < nDeltaState; ++i ) + { + CDmeVertexDeltaData *pDelta = pMesh->GetDeltaState( i ); + MirrorDelta( pDelta, axis, posMirrorMap, normalMirrorMap, uvMirrorMap ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +inline void MirrorData( Vector &d, const int &axis ) +{ + d[ axis ] *= -1.0f; +} + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +inline void MirrorData( Vector2D &d, const int &axis ) +{ + d[ axis ] = ( d[ axis ] - 0.5f ) * -1.0f + 0.5f; +} + + +//----------------------------------------------------------------------------- +// Mirror 3D things like positions & normals +//----------------------------------------------------------------------------- +template < class T_t > +void MirrorVertexData( + CDmeVertexData *pBase, + FieldIndex_t fieldIndex, + int axis, + int nOrigVertexCount, + int nMirrorCount, + const CDmrArrayConst< T_t > &origData, + const CUtlVector< int > &origIndices, + const CUtlVector< int > &mirrorMap, + const CUtlVector< int > &dataMirrorMap ) +{ + if ( nMirrorCount <= 0 ) + return; + + Assert( origIndices.Count() == nOrigVertexCount + nMirrorCount ); + Assert( mirrorMap.Count() == nOrigVertexCount ); + Assert( dataMirrorMap.Count() == origData.Count() ); + + const int nData = origData.Count(); + T_t *pMirrorData = reinterpret_cast< T_t * >( alloca( nMirrorCount * sizeof( T_t ) ) ); + int *pMirrorIndices = reinterpret_cast< int * >( alloca( nMirrorCount * sizeof( int ) ) ); + + T_t mirrorData; + int nMirrorIndex = 0; + int nMirrorDataCount = -1; + for ( int i = 0; i < nOrigVertexCount; ++i ) + { + if ( mirrorMap[ i ] != i ) + { + // Vertex must be mirrored + + if ( dataMirrorMap[ origIndices[ i ] ] != origIndices[ i ] ) + { + // Data referred to by vertex i must be mirror (this may be done a redundant number of times) + const T_t &origDataRef( origData[ origIndices[ i ] ] ); + mirrorData = origDataRef; + MirrorData( mirrorData, axis ); + pMirrorData[ dataMirrorMap[ origIndices[ i ] ] - nData ] = mirrorData; + if ( ( dataMirrorMap[ origIndices[ i ] ] - nData ) > nMirrorDataCount ) + { + nMirrorDataCount = dataMirrorMap[ origIndices[ i ] ] - nData; + } + pMirrorIndices[ nMirrorIndex ] = dataMirrorMap[ origIndices[ i ] ]; + } + else + { + // The data does not need to be mirrored + pMirrorIndices[ nMirrorIndex ] = origIndices[ i ]; + } + + ++nMirrorIndex; + } + else + { + Assert( dataMirrorMap[ origIndices[ i ] ] == origIndices[ i ] ); + } + } + ++nMirrorDataCount; + + Assert( nMirrorCount == nMirrorIndex ); + Assert( nMirrorDataCount <= nMirrorCount ); + + const DmAttributeType_t dmAttributeType = ArrayTypeToValueType( origData.GetAttribute()->GetType() ); + + pBase->AddVertexData( fieldIndex, nMirrorDataCount ); + pBase->SetVertexData( fieldIndex, nData, nMirrorDataCount, dmAttributeType, pMirrorData ); + + pBase->SetVertexIndices( fieldIndex, nOrigVertexCount, nMirrorCount, pMirrorIndices ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::MirrorVertices( + CDmeVertexData *pBase, + int axis, + int nOldVertexCount, + int nMirrorCount, + const CUtlVector< int > &mirrorMap, + const CUtlVector< int > &posMirrorMap, + const CUtlVector< int > &normalMirrorMap, + const CUtlVector< int > &uvMirrorMap ) +{ + if ( !pBase || axis < kXAxis || axis > kZAxis ) + return false; + + pBase->AddVertexIndices( nMirrorCount ); + + const int posFieldIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + const int normalFieldIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ); + const int uvFieldIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ); + + const int nFields = pBase->FieldCount(); + for ( int i = 0; i < nFields; ++i ) + { + CDmAttribute *pBaseData( pBase->GetVertexData( i ) ); + const CUtlVector< int > &baseIndices( pBase->GetVertexIndexData( i ) ); + Assert( baseIndices.Count() == nOldVertexCount + nMirrorCount ); + Assert( mirrorMap.Count() == nOldVertexCount ); + + switch ( pBaseData->GetType() ) + { + case AT_VECTOR2_ARRAY: + if ( i == uvFieldIndex ) + { + MirrorVertexData( pBase, i, axis % 2, nOldVertexCount, nMirrorCount, CDmrArrayConst< Vector2D >( pBaseData ), baseIndices, mirrorMap, uvMirrorMap ); + continue; + } + break; + case AT_VECTOR3_ARRAY: + if ( i == posFieldIndex ) + { + MirrorVertexData( pBase, i, axis, nOldVertexCount, nMirrorCount, CDmrArrayConst< Vector >( pBaseData ), baseIndices, mirrorMap, posMirrorMap ); + continue; + } + else if ( i == normalFieldIndex ) + { + MirrorVertexData( pBase, i, axis, nOldVertexCount, nMirrorCount, CDmrArrayConst< Vector >( pBaseData ), baseIndices, mirrorMap, normalMirrorMap ); + continue; + } + break; + default: + break; + } + + MirrorVertices( pBase, i, nOldVertexCount, nMirrorCount, baseIndices, mirrorMap ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// This does the default case of mirroring which is no mirroring at all! +// No data is changed, the extra indices are added to the index +//----------------------------------------------------------------------------- +void CDmMeshUtils::MirrorVertices( + CDmeVertexData *pBase, + FieldIndex_t fieldIndex, + int nOldVertexCount, + int nMirrorCount, + const CUtlVector< int > &baseIndices, + const CUtlVector< int > &mirrorMap ) +{ + if ( nMirrorCount <= 0 ) + return; + + Assert( baseIndices.Count() == nOldVertexCount + nMirrorCount ); + Assert( mirrorMap.Count() == nOldVertexCount ); + int *pIndices = reinterpret_cast< int * >( alloca( nMirrorCount * sizeof( int ) ) ); + + { + int pIndex = 0; + for ( int i = 0; i < nOldVertexCount; ++i ) + { + if ( mirrorMap[ i ] != i ) + { + pIndices[ pIndex ] = baseIndices[ mirrorMap[ i ] - nOldVertexCount ]; + ++pIndex; + } + } + Assert( pIndex == nMirrorCount ); + } + + pBase->SetVertexIndices( fieldIndex, nOldVertexCount, nMirrorCount, pIndices ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template < class T_t > +void MirrorDeltaData( + CDmeVertexDeltaData *pDelta, + FieldIndex_t fieldIndex, + int axis, + const CDmrArrayConst< T_t > &origData, + const CUtlVector< int > &origIndices, + const CUtlVector< int > &dataMap ) +{ + Assert( origData.Count() == origIndices.Count() ); + + const int nOrigDataCount = origData.Count(); + + T_t *pMirrorData = reinterpret_cast< T_t * >( alloca( nOrigDataCount * sizeof( T_t ) ) ); + int *pMirrorIndices = reinterpret_cast< int * >( alloca( nOrigDataCount * sizeof( int ) ) ); + + int nMirrorDataCount = 0; + for ( int i = 0; i < nOrigDataCount; ++i ) + { + if ( dataMap[ origIndices[ i ] ] != origIndices[ i ] ) + { + pMirrorData[ nMirrorDataCount ] = origData[ i ]; + MirrorData( pMirrorData[ nMirrorDataCount ], axis ); + pMirrorIndices[ nMirrorDataCount ] = dataMap[ origIndices[ i ] ]; + ++nMirrorDataCount; + } + } + + const DmAttributeType_t dmAttributeType = ArrayTypeToValueType( origData.GetAttribute()->GetType() ); + + pDelta->AddVertexData( fieldIndex, nMirrorDataCount ); + pDelta->SetVertexData( fieldIndex, nOrigDataCount, nMirrorDataCount, dmAttributeType, pMirrorData ); + pDelta->SetVertexIndices( fieldIndex, nOrigDataCount, nMirrorDataCount, pMirrorIndices ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::MirrorDelta( + CDmeVertexDeltaData *pDelta, + int axis, + const CUtlVector< int > &posMirrorMap, + const CUtlVector< int > &normalMirrorMap, + const CUtlVector< int > &uvMirrorMap ) +{ + if ( !pDelta || axis < kXAxis || axis > kZAxis ) + return false; + + const int posFieldIndex = pDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + const int normalFieldIndex = pDelta->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ); + const int uvFieldIndex = pDelta->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ); + + const int nFields = pDelta->FieldCount(); + for ( int i = 0; i < nFields; ++i ) + { + CDmAttribute *pDeltaData( pDelta->GetVertexData( i ) ); + const CUtlVector< int > &deltaIndices( pDelta->GetVertexIndexData( i ) ); + + switch ( pDeltaData->GetType() ) + { + case AT_VECTOR2_ARRAY: + if ( i == uvFieldIndex ) + { + MirrorDeltaData( pDelta, i, axis % 2, CDmrArrayConst< Vector2D >( pDeltaData ), deltaIndices, uvMirrorMap ); + continue; + } + break; + case AT_VECTOR3_ARRAY: + if ( i == posFieldIndex ) + { + MirrorDeltaData( pDelta, i, axis, CDmrArrayConst< Vector >( pDeltaData ), deltaIndices, posMirrorMap ); + continue; + } + else if ( i == normalFieldIndex ) + { + MirrorDeltaData( pDelta, i, axis, CDmrArrayConst< Vector >( pDeltaData ), deltaIndices, normalMirrorMap ); + continue; + } + break; + default: + break; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Finds all materials bound to the mesh and replaces ones which match the +// source name with the destination name +//----------------------------------------------------------------------------- +bool CDmMeshUtils::RemapMaterial( CDmeMesh *pMesh, const CUtlString &src, const CUtlString &dst ) +{ + bool retVal = false; + + char srcName[ MAX_PATH ]; + char matName[ MAX_PATH ]; + char dstName[ MAX_PATH ]; + + Q_StripExtension( src.Get(), srcName, sizeof( srcName ) ); + Q_FixSlashes( srcName, '/' ); + + Q_strncpy( dstName, dst.Get(), sizeof( dstName ) ); + Q_FixSlashes( dstName, '/' ); + + const int nFaceSets = pMesh->FaceSetCount(); + for ( int i = 0; i < nFaceSets; ++i ) + { + CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i ); + if ( !pFaceSet ) + continue; + + CDmeMaterial *pMaterial = pFaceSet->GetMaterial(); + if ( !pMaterial ) + continue; + + const char *pMaterialName = pMaterial->GetMaterialName(); + Q_StripExtension( pMaterialName, matName, sizeof( matName ) ); + Q_FixSlashes( matName, '/' ); + + // TODO: Regular expressions or at least glob style matching would be cool + if ( !Q_stricmp( srcName, matName ) ) + { + pMaterial->SetMaterial( dstName ); + pMaterial->SetName( dstName ); + retVal = true; + } + } + + return retVal; +} + + +//----------------------------------------------------------------------------- +// Replaces the nth material found with the specified material name +//----------------------------------------------------------------------------- +bool CDmMeshUtils::RemapMaterial( CDmeMesh *pMesh, const int nMaterialIndex, const CUtlString &dst ) +{ + const int nFaceSets = pMesh->FaceSetCount(); + if ( nMaterialIndex >= nFaceSets ) + return false; + + CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( nMaterialIndex ); + if ( !pFaceSet ) + return false; + + CDmeMaterial *pMaterial = pFaceSet->GetMaterial(); + if ( !pMaterial ) + return false; + + pMaterial->SetMaterial( dst ); + pMaterial->SetName( dst ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Finds the "socket" on which to base the mesh merge +// This is defined as the vertices along the two meshes +// Returns the index into srcBorderEdgesList of the edge list that is found +// -1 if not found +//----------------------------------------------------------------------------- +int CDmMeshUtils::FindMergeSocket( + const CUtlVector< CUtlVector< CDmMeshComp::CEdge * > > &srcBorderEdgesList, + CDmeMesh *pDstMesh ) +{ + CDmMeshComp dstComp( pDstMesh ); + + const CUtlVector< CDmMeshComp::CEdge * > &edgeList = dstComp.m_edges; + + for ( int i = srcBorderEdgesList.Count() - 1; i >= 0; --i ) + { + const CUtlVector< CDmMeshComp::CEdge * > &srcBorderEdges = srcBorderEdgesList[ i ]; + + int nEdgeMatch = 0; + + for ( int j = 0; j != edgeList.Count(); j++ ) + { + const CDmMeshComp::CEdge &e = *edgeList[ j ]; + + for ( int k = srcBorderEdges.Count() - 1; k >= 0; --k ) + { + if ( e == *srcBorderEdges[ k ] ) + { + ++nEdgeMatch; + break; + } + } + } + + if ( nEdgeMatch == srcBorderEdges.Count() ) + { + return i; + } + } + + return -1; +} + + +//----------------------------------------------------------------------------- +// Merge by finding the two meshes in the scene which are joined at a socket +// A socket being defined as a group of border edges that match exactly +// between two meshes +//----------------------------------------------------------------------------- +bool CDmMeshUtils::Merge( CDmeMesh *pSrcMesh, CDmElement *pRoot ) +{ + CDmMeshComp srcComp( pSrcMesh ); + + CUtlVector< CUtlVector< CDmMeshComp::CEdge * > > srcBorderEdgesList; + if ( srcComp.GetBorderEdges( srcBorderEdgesList ) == 0 ) + return false; + + CDmeMesh *pDstMesh = NULL; + + // Find each mesh under pRoot + CDmeDag *pModel = pRoot->GetValueElement< CDmeDag >( "model" ); + if ( !pModel ) + return false; + + CUtlStack< CDmeDag * > traverseStack; + traverseStack.Push( pModel ); + + CDmeDag *pDag; + CDmeMesh *pMesh; + + Vector srcCenter; + float srcRadius; + + Vector dstCenter; + float dstRadius; + + float sqDist = FLT_MAX; + pSrcMesh->GetBoundingSphere( srcCenter, srcRadius ); + + int nEdgeListIndex = -1; + + while ( traverseStack.Count() ) + { + traverseStack.Pop( pDag ); + if ( !pDag ) + continue; + + // Push all children onto stack in reverse order + for ( int nChildIndex = pDag->GetChildCount() - 1; nChildIndex >= 0; --nChildIndex ) + { + traverseStack.Push( pDag->GetChild( nChildIndex ) ); + } + + // See if there's a mesh associated with this dag + pMesh = CastElement< CDmeMesh >( pDag->GetShape() ); + if ( !pMesh ) + continue; + + int eli = FindMergeSocket( srcBorderEdgesList, pMesh ); + if ( eli < 0 ) + continue; + + pMesh->GetBoundingSphere( dstCenter, dstRadius ); + dstRadius = dstCenter.DistToSqr( srcCenter ); + + if ( dstRadius < sqDist ) + { + sqDist = dstRadius; + pDstMesh = pMesh; + nEdgeListIndex = eli; + } + } + + if ( pDstMesh ) + { + return Merge( srcComp, srcBorderEdgesList[ nEdgeListIndex ], pDstMesh ); + } + + Msg( "Error: Merge() - No Merge Socket Found - i.e. A Set Of Border Edges On The Source Model That Are Found On The Merge Model" ); + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template < class T_t > +void AppendData( + const CDmrArrayConst< T_t > &srcData, + CDmrArray< T_t > &dstData, + const matrix3x4_t *pMat = NULL ) +{ + const int nSrcCount = srcData.Count(); + const int nDstCount = dstData.Count(); + + dstData.AddMultipleToTail( nSrcCount ); + dstData.SetMultiple( nDstCount, nSrcCount, srcData.Base() ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template <> +void AppendData( + const CDmrArrayConst< Vector > &srcData, + CDmrArray< Vector > &dstData, + const matrix3x4_t *pMat ) +{ + const int nSrcCount = srcData.Count(); + const int nDstCount = dstData.Count(); + + dstData.AddMultipleToTail( nSrcCount ); + + if ( pMat ) + { + Vector v; + for ( int i = 0; i < nSrcCount; ++i ) + { + v = srcData.Get( i ); + VectorTransform( srcData.Get( i ), *pMat, v ); + dstData.Set( nDstCount + i, v ); + } + } + else + { + dstData.SetMultiple( nDstCount, nSrcCount, srcData.Base() ); + } +} + + +//----------------------------------------------------------------------------- +// Merge data from a base state on one DmeMesh into another DmeMesh +// Preserve positions and normals by transforming them with the +// positionMatrix & normalMatrix +// +// Return the number of new vertices in the mesh +//----------------------------------------------------------------------------- +int MergeBaseState( + CDmeVertexData *pSrcBase, + CDmeVertexData *pDstBase, + const matrix3x4_t &pMat, + const matrix3x4_t &nMat, + int nSkinningJointIndex, + int &nPositionOffset, + int &nNormalOffset, + int &nWrinkleOffset ) +{ + int nRetVal = -1; + + const int nSrcPositionIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + const int nSrcNormalIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ); + const int nSrcWrinkleIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_WRINKLE ); + const int nSrcJointWeightsIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS ); + const int nSrcJointIndicesIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES ); + + const int nDstJointWeightsIndex = pDstBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS ); + const int nDstJointIndicesIndex = pDstBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES ); + + // Handle skinning the new mesh data to a single joint if the destination mesh + // is already skinned. If the destination mesh is skinned but there is no + // specific joint specified to skin to, the first joint is used and a warning issued + + if ( nDstJointWeightsIndex >= 0 && nDstJointIndicesIndex >= 0 ) + { + if ( nSkinningJointIndex < 0 ) + { + Msg( "Warning: Destination mesh is skinned but no valid joint specified to skin to, using first joint\n" ); + nSkinningJointIndex = 0; + } + + const int nJointCount = pDstBase->JointCount(); + + CDmrGenericArray srcPos( pSrcBase->GetVertexData( nSrcPositionIndex ) ); + const int nSrcPosCount = srcPos.Count(); + + CDmrArray< float > dstWeights( pDstBase->GetVertexData( nDstJointWeightsIndex ) ); + CDmrArray< int > dstIndices( pDstBase->GetVertexData( nDstJointIndicesIndex ) ); + + const int nDstCount = dstWeights.Count(); + Assert( nDstCount == dstIndices.Count() ); + + dstWeights.AddMultipleToTail( nSrcPosCount * nJointCount ); + dstIndices.AddMultipleToTail( nSrcPosCount * nJointCount ); + + // Since there can be more than 1 joint per vertex, specify 1 + // for the first joint and 0 for the rest but use the same joint + const int nEnd = nDstCount + nSrcPosCount * nJointCount; + for ( int i = nDstCount; i < nEnd; i += nJointCount ) + { + dstWeights.Set( i, 1.0f ); + dstIndices.Set( i, nSkinningJointIndex ); + } + + for ( int i = 1; i < nJointCount; ++i ) + { + for ( int j = nDstCount + i; j < nEnd; j += nJointCount ) + { + dstWeights.Set( j, 0.0f ); + dstIndices.Set( j, nSkinningJointIndex ); + } + } + } + + // Handling merging all fields that match + int nIndexPadCount = -1; + + for ( int i = 0; i < pSrcBase->FieldCount(); ++i ) + { + bool bMerged = false; + + for ( int j = 0; j < pDstBase->FieldCount(); ++j ) + { + if ( i == nSrcJointWeightsIndex || i == nSrcJointIndicesIndex || Q_strcmp( pSrcBase->FieldName( i ), pDstBase->FieldName( j ) ) ) + continue; + + bMerged = true; + + CDmAttribute *pSrcData = pSrcBase->GetVertexData( i ); + CDmAttribute *pDstData = pDstBase->GetVertexData( j ); + + const int nOffset = CDmrGenericArray( pDstData ).Count(); + + switch ( pSrcData->GetType() ) + { + case AT_FLOAT_ARRAY: + AppendData( CDmrArrayConst< float >( pSrcData ), CDmrArray< float >( pDstData ) ); + break; + case AT_VECTOR2_ARRAY: + AppendData( CDmrArrayConst< Vector2D >( pSrcData ), CDmrArray< Vector2D >( pDstData ) ); + break; + case AT_VECTOR3_ARRAY: + if ( i == nSrcPositionIndex ) + { + AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ), &pMat ); + } + else if ( i == nSrcNormalIndex ) + { + AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ), &nMat ); + } + else + { + AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ) ); + } + break; + case AT_VECTOR4_ARRAY: + AppendData( CDmrArrayConst< Vector4D >( pSrcData ), CDmrArray< Vector4D >( pDstData ) ); + break; + case AT_QUATERNION_ARRAY: + AppendData( CDmrArrayConst< Quaternion >( pSrcData ), CDmrArray< Quaternion >( pDstData ) ); + break; + case AT_COLOR_ARRAY: + AppendData( CDmrArrayConst< Color >( pSrcData ), CDmrArray< Color >( pDstData ) ); + break; + default: + Assert( 0 ); + break; + } + + CDmrArray< int > srcIndices( pSrcBase->GetIndexData( i ) ); + CDmrArray< int > dstIndices( pDstBase->GetIndexData( j ) ); + + const int nSrcIndexCount = srcIndices.Count(); + const int nDstIndexCount = dstIndices.Count(); + + if ( nRetVal < 0 ) + { + nRetVal = nDstIndexCount; + } + Assert( nRetVal == nDstIndexCount ); + + dstIndices.AddMultipleToTail( nSrcIndexCount ); + if ( nIndexPadCount < 0 ) + { + nIndexPadCount = nSrcIndexCount; + } + Assert( nIndexPadCount == nSrcIndexCount ); + + for ( int k = 0; k < nSrcIndexCount; ++k ) + { + dstIndices.Set( nDstIndexCount + k, srcIndices.Get( k ) + nOffset ); + } + + if ( i == nSrcPositionIndex ) + { + nPositionOffset = nOffset; + } + else if ( i == nSrcNormalIndex ) + { + nNormalOffset = nOffset; + } + else if ( i == nSrcWrinkleIndex ) + { + nWrinkleOffset = nOffset; + } + } + + if ( !bMerged ) + { + Msg( "Warning: Not merging base data %s\n", pSrcBase->FieldName( i ) ); + } + } + + const int nDstSpeedIndex = pDstBase->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED ); + + // Handle all fields on the destination mesh that weren't on the source mesh + for ( int i = 0; i < pDstBase->FieldCount(); ++i ) + { + bool bFound = false; + + if ( i == nDstJointWeightsIndex || i == nDstJointIndicesIndex ) + continue; + + for ( int j = 0; j < pSrcBase->FieldCount(); ++j ) + { + if ( Q_strcmp( pDstBase->FieldName( i ), pSrcBase->FieldName( j ) ) ) + continue; + + bFound = true; + break; + } + + if ( !bFound ) + { + int nDstIndex = -1; + + if ( i == nDstSpeedIndex ) + { + // Pad data with a 1 + nDstIndex = CDmrArray< float >( pDstBase->GetVertexData( i ) ).AddToTail( 1.0f ); + } + else + { + // Pad data with a 0 + nDstIndex = CDmrGenericArray( pDstBase->GetVertexData( i ) ).AddToTail(); + } + + // Pad data indices with index to that extra data value + CDmrArray< int > dstIndices( pDstBase->GetIndexData( i ) ); + const int nStart = dstIndices.Count(); + const int nEnd = dstIndices.Count() + nIndexPadCount; + dstIndices.AddMultipleToTail( nIndexPadCount ); + for ( int k = nStart; k < nEnd; ++k ) + { + dstIndices.Set( k, nDstIndex ); + } + } + } + + pDstBase->Resolve(); + + return nRetVal; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void MergeDeltaState( CDmeMesh *pDmeMesh, CDmeVertexDeltaData *pSrcDelta, CDmeVertexDeltaData *pDstDelta, int &nPositionOffset, int &nNormalOffset, int &nWrinkleOffset ) +{ + if ( !pDstDelta ) + { + // No destination delta... copy it + pDstDelta = pDmeMesh->FindOrCreateDeltaState( pSrcDelta->GetName() ); + if ( !pDstDelta ) + return; + } + + for ( int i = 0; i < pSrcDelta->FieldCount(); ++i ) + { + bool bFound = false; + + for ( int j = 0; j < pDstDelta->FieldCount(); ++j ) + { + if ( Q_strcmp( pSrcDelta->FieldName( i ), pDstDelta->FieldName( j ) ) ) + continue; + + bFound = true; + break; + } + + if ( !bFound ) + { + // Make an empty one, data will be added below + CDmAttribute *pSrcData = pSrcDelta->GetVertexData( i ); + pDstDelta->CreateField( pSrcDelta->FieldName( i ), pSrcData->GetType() ); + } + } + + const int nSrcPositionIndex = pSrcDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + const int nSrcNormalIndex = pSrcDelta->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ); + const int nSrcWrinkleIndex = pSrcDelta->FindFieldIndex( CDmeVertexData::FIELD_WRINKLE ); + + for ( int i = 0; i < pSrcDelta->FieldCount(); ++i ) + { + int nOffset = 0; + + if ( i == nSrcPositionIndex ) + { + nOffset = nPositionOffset; + } + else if ( i == nSrcNormalIndex ) + { + nOffset = nNormalOffset; + } + else if ( i == nSrcWrinkleIndex ) + { + nOffset = nWrinkleOffset; + } + + if ( nOffset < 0 ) + { + nOffset = 0; + } + + for ( int j = 0; j < pDstDelta->FieldCount(); ++j ) + { + if ( Q_strcmp( pSrcDelta->FieldName( i ), pDstDelta->FieldName( j ) ) ) + continue; + + CDmAttribute *pSrcData = pSrcDelta->GetVertexData( i ); + CDmAttribute *pDstData = pDstDelta->GetVertexData( j ); + + switch ( pSrcData->GetType() ) + { + case AT_FLOAT_ARRAY: + AppendData( CDmrArrayConst< float >( pSrcData ), CDmrArray< float >( pDstData ) ); + break; + case AT_VECTOR2_ARRAY: + AppendData( CDmrArrayConst< Vector2D >( pSrcData ), CDmrArray< Vector2D >( pDstData ) ); + break; + case AT_VECTOR3_ARRAY: + AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ) ); + break; + case AT_VECTOR4_ARRAY: + AppendData( CDmrArrayConst< Vector4D >( pSrcData ), CDmrArray< Vector4D >( pDstData ) ); + break; + case AT_QUATERNION_ARRAY: + AppendData( CDmrArrayConst< Quaternion >( pSrcData ), CDmrArray< Quaternion >( pDstData ) ); + break; + case AT_COLOR_ARRAY: + AppendData( CDmrArrayConst< Color >( pSrcData ), CDmrArray< Color >( pDstData ) ); + break; + default: + Assert( 0 ); + break; + } + + CDmrArray< int > srcIndices( pSrcDelta->GetIndexData( i ) ); + CDmrArray< int > dstIndices( pDstDelta->GetIndexData( j ) ); + + const int nSrcIndexCount = srcIndices.Count(); + const int nDstIndexCount = dstIndices.Count(); + + dstIndices.AddMultipleToTail( nSrcIndexCount ); + + for ( int k = 0; k < nSrcIndexCount; ++k ) + { + dstIndices.Set( nDstIndexCount + k, srcIndices.Get( k ) + nOffset ); + } + + break; + } + } + + // TODO: Centralize all of the '_' for corrector business... + const char *pszDeltaName = pDstDelta->GetName(); + if ( strchr( pszDeltaName, '_' ) ) + return; // No controls for deltas with '_''s + + if ( !pDmeMesh ) + return; + + CDmeCombinationOperator *pDmeCombo = FindReferringElement< CDmeCombinationOperator >( pDmeMesh, "targets" ); + if ( !pDmeCombo ) + return; + + if ( pDmeCombo->HasRawControl( pszDeltaName ) ) + return; + + pDmeCombo->FindOrCreateControl( pDstDelta->GetName(), false, true ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void GetAbsTransform( CDmeDag *pDmeDag, matrix3x4_t &m ) +{ + matrix3x4_t mParentAbsTransform; + pDmeDag->GetParentWorldMatrix( mParentAbsTransform ); + + matrix3x4_t mLocal; + CDmeTransform *pDmeTransform = pDmeDag->GetTransform(); + if ( pDmeTransform ) + { + pDmeTransform->GetTransform( mLocal ); + } + else + { + SetIdentityMatrix( mLocal ); + } + + ConcatTransforms( mParentAbsTransform, mLocal, m ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::Merge( CDmeMesh *pSrcMesh, CDmeMesh *pDstMesh, int nSkinningJointIndex ) +{ + if ( !pSrcMesh || !pDstMesh ) + return false; + + CDmeDag *pSrcDag = FindReferringElement< CDmeDag >( pSrcMesh, "shape", true ); + CDmeDag *pDstDag = FindReferringElement< CDmeDag >( pDstMesh, "shape", true ); + + if ( !pSrcDag || !pDstDag ) + return false; + + matrix3x4_t nMat; + GetAbsTransform( pSrcDag, nMat ); + matrix3x4_t pMat; + GetAbsTransform( pDstDag, pMat ); + matrix3x4_t dMatInv; + + MatrixInvert( pMat, dMatInv ); + MatrixMultiply( dMatInv, nMat, pMat ); + MatrixInverseTranspose( pMat, nMat ); + + int nPositionOffset = -1; + int nNormalOffset = -1; + int nWrinkleOffset = -1; + + int nVertexOffset = -1; + + for ( int i = 0; i < pSrcMesh->BaseStateCount(); ++i ) + { + CDmeVertexData *pSrcBase = pSrcMesh->GetBaseState( i ); + bool bMerged = false; + + for ( int j = 0; j < pDstMesh->BaseStateCount(); ++j ) + { + CDmeVertexData *pDstBase = pDstMesh->GetBaseState( j ); + + if ( Q_strcmp( pSrcBase->GetName(), pDstBase->GetName() ) ) + continue; + + bMerged = true; + const int nTmpVertexOffset = MergeBaseState( pSrcBase, pDstBase, pMat, nMat, nSkinningJointIndex, nPositionOffset, nNormalOffset, nWrinkleOffset ); + if ( nVertexOffset < 0 ) + { + nVertexOffset = nTmpVertexOffset; + } + + Assert( nVertexOffset == nTmpVertexOffset ); + } + + if ( !bMerged ) + { + Msg( "Error: Merge( %s, %s ) - Can't Find Base State %s On %s\n", pSrcMesh->GetName(), pDstMesh->GetName(), pSrcBase->GetName(), pDstMesh->GetName() ); + } + } + + // Merge Face Sets + + int nFaceSetIndex; + + for ( int i = 0; i < pSrcMesh->FaceSetCount(); ++i ) + { + CDmeFaceSet *pFaceSet = pSrcMesh->GetFaceSet( i )->Copy(); + pFaceSet->SetFileId( pDstMesh->GetFileId(), TD_DEEP ); + const int nFaceSetIndexCount = pFaceSet->NumIndices(); + for ( int j = 0; j < nFaceSetIndexCount; ++j ) + { + nFaceSetIndex = pFaceSet->GetIndex( j ); + if ( nFaceSetIndex >= 0 ) + { + pFaceSet->SetIndex( j, nFaceSetIndex + nVertexOffset ); + } + } + pDstMesh->AddFaceSet( pFaceSet ); + } + + // Merge Deltas + + for ( int i = 0; i < pSrcMesh->DeltaStateCount(); ++i ) + { + CDmeVertexDeltaData *pSrcDelta = pSrcMesh->GetDeltaState( i ); + CDmeVertexDeltaData *pDstDelta = pDstMesh->FindDeltaState( pSrcDelta->GetName() ); + MergeDeltaState( pDstMesh, pSrcDelta, pDstDelta, nPositionOffset, nNormalOffset, nWrinkleOffset ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +struct VertexWeightMap_s +{ + struct VertexWeight_s + { + int m_vertexDataIndex; // Index into the CDmeVertexData data (only used for joint weights & indices) + const CUtlVector< int > *m_pVertexIndices; // Index into the CDmeVertexData vertex indices + float m_vertexWeight; + }; + + int m_nVertexWeights; + VertexWeight_s m_vertexWeights[ 5 ]; +}; + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CopyJointWeights( + CDmeVertexData *pSrcData, + CDmeVertexData *pDstData, + const CUtlVector< VertexWeightMap_s > &vertexWeightMap ) +{ + const int nJointCount = pSrcData->GetValue< int >( "jointCount" ); + + const FieldIndex_t nSrcJointWeightsField = pSrcData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS ); + const FieldIndex_t nSrcJointIndicesField = pSrcData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES ); + + if ( nJointCount <= 0 || nSrcJointWeightsField < 0 || nSrcJointIndicesField < 0 ) + return false; + + const CUtlVector< float > &srcJointWeights = CDmrArrayConst< float >( pSrcData->GetVertexData( nSrcJointWeightsField ) ).Get(); + const float *const pSrcJointWeights = srcJointWeights.Base(); + + const CUtlVector< int > &srcJointIndices = CDmrArrayConst< int >( pSrcData->GetVertexData( nSrcJointIndicesField ) ).Get(); + const int *const pSrcJointIndices = srcJointIndices.Base(); + + FieldIndex_t nDstJointWeightsField; + FieldIndex_t nDstJointIndicesField; + + pDstData->CreateJointWeightsAndIndices( nJointCount, &nDstJointWeightsField, &nDstJointIndicesField ); + + const int nDstCount = vertexWeightMap.Count(); + + float *pDstJointWeights = reinterpret_cast< float * >( alloca( nDstCount * nJointCount * sizeof( float ) ) ); + memset( pDstJointWeights, 0, nDstCount * nJointCount ); + + int *pDstJointIndices = reinterpret_cast< int * >( alloca( nDstCount * nJointCount * sizeof( int ) ) ); + memset( pDstJointIndices, 0, nDstCount * nJointCount ); + + for ( int i = 0; i < nDstCount; ++i ) + { + const VertexWeightMap_s &vertexWeight = vertexWeightMap[ i ]; + const int nVertexWeights = vertexWeight.m_nVertexWeights; + + if ( nVertexWeights > 0 ) + { + // TODO: Find the best weights to use! For now, use the first one + int nMatchIndex = vertexWeight.m_vertexWeights[ 0 ].m_vertexDataIndex; + + memcpy( pDstJointWeights + i * nJointCount, pSrcJointWeights + nMatchIndex * nJointCount, nJointCount * sizeof( float ) ); + memcpy( pDstJointIndices + i * nJointCount, pSrcJointIndices + nMatchIndex * nJointCount, nJointCount * sizeof( int ) ); + } + } + + pDstData->AddVertexData( nDstJointIndicesField, nDstCount * nJointCount ); + pDstData->SetVertexData( nDstJointIndicesField, 0, nDstCount * nJointCount, AT_INT, pDstJointIndices ); + + pDstData->AddVertexData( nDstJointWeightsField, nDstCount * nJointCount ); + pDstData->SetVertexData( nDstJointWeightsField, 0, nDstCount * nJointCount, AT_FLOAT, pDstJointWeights ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Replaces the DstMesh with the SrcMesh +//----------------------------------------------------------------------------- +CDmeMesh *ReplaceMesh( + CDmeMesh *pSrcMesh, + CDmeMesh *pDstMesh ) +{ + if ( !pSrcMesh || !pDstMesh ) + return NULL; + + CDmeDag *pSrcDag = pSrcMesh->GetParent(); + CDmeDag *pDstDag = pDstMesh->GetParent(); + + if ( !pSrcDag || !pDstDag ) + return NULL; + + // Fix up the transform + matrix3x4_t inclusiveMat; + matrix3x4_t localMat; + + pDstDag->GetShapeToWorldTransform( inclusiveMat ); + pDstDag->GetTransform()->GetTransform( localMat ); + + matrix3x4_t inverseMat; + MatrixInvert( localMat, inverseMat ); + + matrix3x4_t exclusiveMat; + MatrixMultiply( inclusiveMat, inverseMat, exclusiveMat ); + MatrixInvert( exclusiveMat, inverseMat ); + + pSrcDag->GetShapeToWorldTransform( inclusiveMat ); + MatrixMultiply( inverseMat, inclusiveMat, localMat ); + + pDstDag->GetTransform()->SetTransform( localMat ); + + // Duplicate the mesh + CDmeMesh *pNewMesh = pSrcMesh->Copy(); + pNewMesh->SetFileId( pDstMesh->GetFileId(), TD_DEEP ); + + // A bit of cleanup + pNewMesh->RemoveAttribute( "selection" ); + pNewMesh->SetCurrentBaseState( "bind" ); + pNewMesh->DeleteBaseState( "__dmxEdit_work" ); + + // Replace the DstMesh with the SrcMesh + pDstDag->SetShape( pNewMesh ); + + // Replace the combination operators, if applicable + CDmeCombinationOperator *pSrcComboOp = FindReferringElement< CDmeCombinationOperator >( pSrcMesh, "targets" ); + if ( pSrcComboOp ) + { + CDmeCombinationOperator *pDstComboOp = FindReferringElement< CDmeCombinationOperator >( pDstMesh, "targets" ); + CDmElement *pDstRoot = NULL; + if ( pDstComboOp ) + { + // Find the root the easy way + pDstRoot = FindReferringElement< CDmElement >( pDstComboOp, "combinationOperator" ); + + // Delete the old busted combination operator + g_pDataModel->DestroyElement( pDstComboOp->GetHandle() ); + } + else + { + // Find the root the hard way + CDmeDag *pDmeDag = pDstDag; + for ( ;; ) + { + // Walk backwards via "children" attribute + CDmeDag *pNextDag = FindReferringElement< CDmeDag >( pDmeDag, "children" ); + if ( pNextDag ) + { + pDmeDag = pNextDag; + } + else + { + // Can't find anyone referring to this via "children" so, hopefully it's the DmeModel referred to by "model" + pDstRoot = FindReferringElement< CDmElement >( pDmeDag, "model" ); + break; + } + } + } + + if ( pDstRoot ) + { + // Install the shiny new combination operator + CDmeCombinationOperator *pNewComboOp = pSrcComboOp->Copy(); + pNewComboOp->SetFileId( pDstRoot->GetFileId(), TD_DEEP ); + pDstRoot->SetValue( "combinationOperator", pNewComboOp ); + pNewComboOp->RemoveAllTargets(); + pNewComboOp->AddTarget( pNewMesh ); + pNewComboOp->GenerateWrinkleDeltas( false ); + + } + } + + return pNewMesh; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +template < class T_t > +void CopyFieldData( + const CDmrArrayConst< T_t > &srcData, + const CUtlVector< int > &srcIndices, + CDmeVertexData *pDstVertexData, + FieldIndex_t dstFieldIndex, + const CUtlVector< VertexWeightMap_s > &vertexWeightMap ) +{ + const int nDstData = vertexWeightMap.Count(); + + T_t sum; + + T_t *pDstData = reinterpret_cast< T_t * >( alloca( nDstData * sizeof( T_t ) ) ); + + for ( int i = 0; i < nDstData; ++i ) + { + CDmAttributeInfo< T_t >::SetDefaultValue( pDstData[ i ] ); + + const VertexWeightMap_s &vertexWeight = vertexWeightMap[ i ]; + for ( int j = 0; j < vertexWeight.m_nVertexWeights; ++j ) + { + const VertexWeightMap_s::VertexWeight_s &vWeight = vertexWeight.m_vertexWeights[ j ]; + + CDmAttributeInfo< T_t >::SetDefaultValue( sum ); + + const CUtlVector< int > &vertexList = *vWeight.m_pVertexIndices; + for ( int k = 0; k < vertexList.Count(); ++k ) + { + sum += srcData[ srcIndices[ vertexList[ k ] ] ]; + } + sum /= static_cast< float >( vertexList.Count() ); + + pDstData[ i ] += sum * vWeight.m_vertexWeight; + } + } + + const DmAttributeType_t dmAttributeType = ArrayTypeToValueType( srcData.GetAttribute()->GetType() ); + CDmrArray< T_t > dstData( pDstVertexData->GetVertexData( dstFieldIndex ) ); + dstData.EnsureCount( nDstData ); + pDstVertexData->SetVertexData( dstFieldIndex, 0, nDstData, dmAttributeType, pDstData ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CopyField( + CDmeVertexData::StandardFields_t field, + CDmeVertexData *pSrcData, + CDmeVertexData *pDstData, + const CUtlVector< VertexWeightMap_s > &vertexWeightMap ) +{ + FieldIndex_t srcFieldIndex = pSrcData->FindFieldIndex( field ); + if ( srcFieldIndex < 0 ) + return false; + + FieldIndex_t dstFieldIndex = pDstData->CreateField( field ); + if ( dstFieldIndex < 0 ) + return false; + + CDmAttribute *pSrcVertexData = pSrcData->GetVertexData( srcFieldIndex ); + const CUtlVector< int > &srcIndices = pSrcData->GetVertexIndexData( srcFieldIndex ); + + // Everything on dst has to be indexed the same as position + const CUtlVector< int > &dstPosIndices = pDstData->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ); + CDmrArray< int > dstIndices( pDstData->GetIndexData( dstFieldIndex ) ); + dstIndices.EnsureCount( dstPosIndices.Count() ); + pDstData->SetVertexIndices( dstFieldIndex, 0, dstPosIndices.Count(), dstPosIndices.Base() ); + + + switch ( pSrcVertexData->GetType() ) + { + case AT_FLOAT_ARRAY: + CopyFieldData( CDmrArrayConst< float >( pSrcVertexData ), srcIndices, pDstData, dstFieldIndex, vertexWeightMap ); + break; + case AT_VECTOR2_ARRAY: + CopyFieldData( CDmrArrayConst< Vector2D >( pSrcVertexData ), srcIndices, pDstData, dstFieldIndex, vertexWeightMap ); + break; + case AT_VECTOR3_ARRAY: + CopyFieldData( CDmrArrayConst< Vector >( pSrcVertexData ), srcIndices, pDstData, dstFieldIndex, vertexWeightMap ); + break; + default: + break; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::Merge( + CDmMeshComp &srcComp, + const CUtlVector< CDmMeshComp::CEdge * > &edgeList, + CDmeMesh *pDstMesh ) +{ + CDmeMesh *pSrcMesh = srcComp.m_pMesh; + if ( !pSrcMesh || !pDstMesh ) + return false; + + CDmeVertexData *pSrcData = pSrcMesh->FindBaseState( "bind" ); + CDmeVertexData *pDstData = pDstMesh->FindBaseState( "bind" ); + + if ( !pSrcData || !pDstData ) + return false; + + const CUtlVector< Vector > &srcPosData = pSrcData->GetPositionData(); + const int nSrcCount = srcPosData.Count(); + + const CUtlVector< Vector > &dstPosData = pDstData->GetPositionData(); + const int nDstCount = dstPosData.Count(); + + if ( nSrcCount <= 0 || nDstCount <= 0 ) + return false; + + CUtlVector< VertexWeightMap_s > vertexWeightMap; + vertexWeightMap.SetSize( nSrcCount ); + + for ( int i = 0; i < nSrcCount; ++i ) + { + int nClosestIndex = -1; + float closest = FLT_MAX; + + VertexWeightMap_s &vertexWeight = vertexWeightMap[ i ]; + vertexWeight.m_nVertexWeights = 0; + + const Vector &vSrc = srcPosData[ i ]; + + for ( int j = 0; j < nDstCount; ++j ) + { + const Vector &vDst = dstPosData[ j ]; + if ( vSrc.DistToSqr( vDst ) < FLT_EPSILON * 10.0f ) + { + vertexWeight.m_nVertexWeights = 1; + vertexWeight.m_vertexWeights[ 0 ].m_vertexDataIndex = j; + vertexWeight.m_vertexWeights[ 0 ].m_vertexWeight = 1.0f; + vertexWeight.m_vertexWeights[ 0 ].m_pVertexIndices = &pDstData->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, j ); + break; + } + + float distance = vSrc.DistToSqr( vDst ); + if ( distance < closest ) + { + closest = distance; + nClosestIndex = j; + } + } + + if ( vertexWeight.m_nVertexWeights == 0 ) + { + Warning( "Warning: Merge() - No Match For Src Vertex: %f %f %f, Using Closest: %f %f %f\n", + vSrc.x, vSrc.y, vSrc.z, + dstPosData[ nClosestIndex ].x, dstPosData[ nClosestIndex ].y, dstPosData[ nClosestIndex ].z ); + + // TODO: Loop through and find up to n closest vertices by position + + vertexWeight.m_nVertexWeights = 1; + vertexWeight.m_vertexWeights[ 0 ].m_vertexDataIndex = nClosestIndex; + vertexWeight.m_vertexWeights[ 0 ].m_vertexWeight = 1.0f; + vertexWeight.m_vertexWeights[ 0 ].m_pVertexIndices = &pDstData->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, nClosestIndex ); + + // Assert( vertexWeight.m_nVertexWeights ); + // return false; + } + } + + CDmeMesh *pNewMesh = ReplaceMesh( pSrcMesh, pDstMesh ); + if ( !pNewMesh ) + { + Error( "Error: Merge() - Couldn't Replace Mesh %s With %s\n", pDstMesh->GetName(), pSrcMesh->GetName() ); + return false; + } + + CDmeVertexData *pNewData = pNewMesh->FindBaseState( "bind" ); + if ( pNewData ) + { + CopyJointWeights( pDstData, pNewData, vertexWeightMap ); + + CopyField( CDmeVertexData::FIELD_BALANCE, pDstData, pNewData, vertexWeightMap ); + + CopyField( CDmeVertexData::FIELD_MORPH_SPEED, pDstData, pNewData, vertexWeightMap ); + + if ( pNewData->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED ) >= 0 ) + { + CDmeCombinationOperator *pComboOp( FindReferringElement< CDmeCombinationOperator >( pNewMesh, "targets" ) ); + if ( pComboOp ) + { + pComboOp->UsingLaggedData( true ); + } + } + } + + // Destroy the old busted mesh + g_pDataModel->DestroyElement( pDstMesh->GetHandle() ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Returns a guaranteed unique DmFileId_t +//----------------------------------------------------------------------------- +DmFileId_t CreateUniqueFileId() +{ + DmFileId_t fileId = DMFILEID_INVALID; + + UniqueId_t uniqueId; + char fileIdBuf[ MAX_PATH ]; + + do + { + CreateUniqueId( &uniqueId ); + UniqueIdToString( uniqueId, fileIdBuf, sizeof( fileIdBuf ) ); + + fileId = g_pDataModel->GetFileId( fileIdBuf ); + } while( fileId != DMFILEID_INVALID ); + + return g_pDataModel->FindOrCreateFileId( fileIdBuf ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CreateExpressionFile( const char *pExpressionFile, const CUtlVector< CUtlString > *pPurgeAllButThese, CDmeCombinationOperator *pComboOp, CDmePresetGroup *pPresetGroup ) +{ + if ( !pPresetGroup ) + return false; + + Assert( pExpressionFile && pComboOp ); + + const int nControlCount = pComboOp->GetControlCount(); + + const CDmaElementArray< CDmePreset > &presets = pPresetGroup->GetPresets(); + const int nPresetsCount = presets.Count(); + + if ( nControlCount <= 0 || nPresetsCount <= 0 ) + return false; + + char expName[ MAX_PATH ]; + Q_FileBase( pExpressionFile, expName, sizeof( expName ) ); + + CDmePresetGroup *pDstPresetGroup = CreateElement< CDmePresetGroup >( expName, CreateUniqueFileId() ); + if ( !pDstPresetGroup ) + return false; + + for ( int i = 0; i < nPresetsCount; ++i ) + { + CDmePreset *pPreset = presets[ i ]; + const char *pPresetName = pPreset->GetName(); + CDmePreset *pDstPreset = pDstPresetGroup->FindOrAddPreset( pPresetName ); + + const CDmaElementArray< CDmElement > &controlValues = pPreset->GetControlValues(); + const int nControlValueCount = controlValues.Count(); + + for ( int j = 0; j < nControlCount; ++j ) + { + // Figure out if this preset is used + bool bFound = false; // Used for two things + const char *pControlName = pComboOp->GetControlName( j ); + + if ( pPurgeAllButThese ) + { + for ( int k = 0; k < pPurgeAllButThese->Count(); ++k ) + { + if ( !Q_strcmp( pControlName, pPurgeAllButThese->Element( k ).Get() ) ) + { + bFound = true; + break; + } + } + } + + if ( !bFound && pPresetGroup->FindPreset( pControlName ) ) + bFound = true; + + if ( !bFound ) + continue; + + CDmElement *pDstControlValue = NULL; + + const bool bStereo = pComboOp->IsStereoControl( j ); + const bool bMulti = pComboOp->IsMultiControl( j ); + + if ( !Q_strcmp( pControlName, pPresetName ) ) + { + pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName ); + pDstControlValue->SetValue( "value", 1.0f ); + + // These shouldn't really happen because these are presets which were made + // into deltas so they are never stereo nor multi-controls + if ( bStereo ) + { + pDstControlValue->SetValue( "balance", 0.5f ); + } + if ( bStereo ) + { + pDstControlValue->SetValue( "multilevel", 0.5f ); + } + continue; + } + + for ( int k = 0; k < nControlValueCount; ++k ) + { + CDmElement *pControlPreset = controlValues[ k ]; + + if ( !Q_strcmp( pControlName, pControlPreset->GetName() ) ) + { + pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName ); + pDstControlValue->SetValue( "value", pControlPreset->GetValue( "value", 0.0f ) ); + if ( bStereo ) + { + pDstControlValue->SetValue( "balance", pControlPreset->GetValue( "balance", 0.5f ) ); + } + if ( bMulti ) + { + pDstControlValue->SetValue( "multilevel", pControlPreset->GetValue( "multilevel", 0.5f ) ); + } + break; + } + } + + if ( !pDstControlValue ) + { + pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName ); + pDstControlValue->SetValue( "value", pComboOp->GetControlDefaultValue( j ) ); + + if ( bStereo ) + { + pDstControlValue->SetValue( "balance", 0.5f ); + } + if ( bMulti ) + { + pDstControlValue->SetValue( "multilevel", 0.5f ); + } + } + } + } + + char buf[ MAX_PATH ]; + char buf1[ MAX_PATH ]; + Q_strncpy( buf, pExpressionFile, sizeof( buf ) ); + Q_SetExtension( buf, ".txt", sizeof( buf ) ); + Q_ExtractFilePath( buf, buf1, sizeof( buf1 ) ); + Q_FixSlashes( buf1 ); + g_pFullFileSystem->CreateDirHierarchy( buf1 ); + + if ( !g_p4factory->AccessFile( buf )->Edit() ) + { + g_p4factory->AccessFile( buf )->Add(); + } + + pDstPresetGroup->ExportToTXT( buf, NULL, pComboOp ); + + Q_SetExtension( buf, ".vfe", sizeof( buf ) ); + Q_ExtractFilePath( buf, buf1, sizeof( buf1 ) ); + Q_FixSlashes( buf1 ); + g_pFullFileSystem->CreateDirHierarchy( buf1 ); + + if ( !g_p4factory->AccessFile( buf )->Edit() ) + { + g_p4factory->AccessFile( buf )->Add(); + } + + pDstPresetGroup->ExportToVFE( buf, NULL, pComboOp ); + + g_pDataModel->UnloadFile( pDstPresetGroup->GetFileId() ); + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::CreateDeltasFromPresets( + CDmeMesh *pMesh, + CDmeVertexData *pPassedDst, + const CUtlStringMap< CUtlString > &presetExpressionMap, + bool bPurge, + const CUtlVector< CUtlString > *pPurgeAllButThese /*= NULL */ ) +{ + if ( !pMesh ) + return false; + + CDisableUndoScopeGuard sgDisableUndo; + + CUtlStringMap< CDmePreset * > presetMap; + CUtlStringMap< CUtlString > conflictingNames; + + CDmeVertexData *pDst = pPassedDst ? pPassedDst : pMesh->GetCurrentBaseState(); + CDmeVertexData *pBind = pMesh->FindBaseState( "bind" ); + if ( !pDst || !pBind || pDst == pBind ) + return false; + + CDmeCombinationOperator *pComboOp = FindReferringElement< CDmeCombinationOperator >( pMesh, "targets" ); + if ( !pComboOp ) + return false; + + const bool bSavedUsingLagged = pComboOp->IsUsingLaggedData(); + + CUtlVector< CDmePresetGroup * > presetGroups; + + for ( int i = 0; i < presetExpressionMap.GetNumStrings(); ++i ) + { + const char *pPresetFilename = presetExpressionMap.String( i ); + + // Load the preset file + CDmElement *pRoot = NULL; + g_p4factory->AccessFile( pPresetFilename )->Add(); + g_pDataModel->RestoreFromFile( pPresetFilename, NULL, NULL, &pRoot ); + CDmePresetGroup *pPresetGroup = CastElement< CDmePresetGroup >( pRoot ); + + presetGroups.AddToTail( pPresetGroup ); + + if ( !pPresetGroup ) + continue; + + CreateDeltasFromPresetGroup( pPresetGroup, pComboOp, pPurgeAllButThese, pMesh, pDst, conflictingNames, presetMap ); + } + + if ( bPurge ) + { + PurgeUnreferencedDeltas( pMesh, presetMap, pPurgeAllButThese, pComboOp ); + } + + for ( int i = 0; i < presetMap.GetNumStrings(); ++i ) + { + const char *pPresetName = presetMap[ i ]->GetName(); + const int nControlIndex = pComboOp->FindControlIndex( pPresetName ); + if ( nControlIndex < 0 ) + { + pComboOp->FindOrCreateControl( pPresetName, false, true ); + } + else + { + bool bFound = false; + + if ( bPurge ) + { + pComboOp->RemoveAllRawControls( nControlIndex ); + } + else + { + const int nRawControls = pComboOp->GetRawControlCount( nControlIndex ); + for ( int j = 0; j < nRawControls; ++j ) + { + if ( !Q_strcmp( pComboOp->GetRawControlName( nControlIndex, j ), pPresetName ) ) + { + bFound = true; + break; + } + } + } + + if ( !bFound ) + { + pComboOp->AddRawControl( nControlIndex, pPresetName ); + } + } + } + + pComboOp->UsingLaggedData( bSavedUsingLagged ); + pComboOp->SetToDefault(); + + for ( int i = 0; i < presetExpressionMap.GetNumStrings(); ++i ) + { + const CUtlString &expressionFile = presetExpressionMap[ i ]; + if ( expressionFile.IsEmpty() ) + continue; + + CreateExpressionFile( expressionFile.Get(), pPurgeAllButThese, pComboOp, presetGroups[ i ] ); + } + + for ( int i = 0; i < presetGroups.Count(); ++i ) + { + CDmePresetGroup *pPresetGroup = presetGroups[ i ]; + if ( !pPresetGroup ) + continue; + + g_pDataModel->UnloadFile( pPresetGroup->GetFileId() ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Removes any deltas from the specified mesh which are not referred to by +// any rule of the combination operator driving the mesh +//----------------------------------------------------------------------------- +bool CDmMeshUtils::PurgeUnusedDeltas( CDmeMesh *pMesh ) +{ + // Disable for now + // This code will delete all corrective delta states, i.e. deltas named A_B + return true; + + if ( !pMesh ) + return false; + + CDmeCombinationOperator *pCombo = FindReferringElement< CDmeCombinationOperator >( pMesh, "targets" ); + if ( !pCombo ) + return false; + + const int nControlCount = pCombo->GetControlCount(); + + CUtlVector< CDmeMesh::DeltaComputation_t > compList; + pMesh->ComputeDependentDeltaStateList( compList ); + const int nDeltaCount = compList.Count(); + + Assert( nDeltaCount == pMesh->DeltaStateCount() ); + + CUtlVector< bool > deltasToKeep; + deltasToKeep.EnsureCount( nDeltaCount ); + memset( deltasToKeep.Base(), 0, sizeof( bool ) * nDeltaCount ); + + for ( int i = 0; i < nControlCount; ++i ) + { + const int nRawControlCount = pCombo->GetRawControlCount( i ); + for ( int j = 0; j < nRawControlCount; ++j ) + { + const int nDeltaIndex = pMesh->FindDeltaStateIndex( pCombo->GetRawControlName( i, j ) ); + const CDmeMesh::DeltaComputation_t &deltaComp = compList[ nDeltaIndex ]; + deltasToKeep[ deltaComp.m_nDeltaIndex ] = true; + for ( int k = 0; k < deltaComp.m_DependentDeltas.Count(); ++k ) + { + deltasToKeep[ deltaComp.m_DependentDeltas[ k ] ] = true; + } + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshUtils::CreateWrinkleDeltaFromBaseState( + CDmeVertexDeltaData *pDelta, + float flScale /* = 1.0f */, + WrinkleOp wrinkleOp /* = kReplace */, + CDmeMesh *pPassedMesh /* = NULL */, + CDmeVertexData *pPassedBind /* = NULL */, + CDmeVertexData *pPassedCurrent /* = NULL */ ) +{ + CDmeVertexData *pBind = pPassedBind ? pPassedBind : pPassedMesh ? pPassedMesh->GetBindBaseState() : NULL; + CDmeVertexData *pCurr = pPassedCurrent ? pPassedCurrent : pPassedMesh ? pPassedMesh->GetCurrentBaseState() : NULL; + + const CDmeMesh *pMesh = pPassedMesh ? pPassedMesh : pBind ? FindReferringElement< CDmeMesh >( pBind, "baseStates" ) : NULL; + const CDmeMesh *pBindMesh = pBind ? FindReferringElement< CDmeMesh >( pBind, "baseStates" ) : NULL; + const CDmeMesh *pCurrMesh = pCurr ? FindReferringElement< CDmeMesh >( pCurr, "baseStates", false ) : NULL; + const CDmeMesh *pDeltaMesh = pDelta ? FindReferringElement< CDmeMesh >( pDelta, "deltaStates" ) : NULL; + + if ( !pDelta || !pBind || !pCurr || pBind == pCurr || !pMesh || pMesh != pBindMesh || pMesh != pCurrMesh || pMesh != pDeltaMesh ) + { + return false; + } + + const FieldIndex_t nBindPosIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + const FieldIndex_t nBindTexIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ); + const FieldIndex_t nCurrPosIndex = pCurr->FindFieldIndex( CDmeVertexData::FIELD_POSITION ); + + if ( nBindPosIndex < 0 || nBindTexIndex < 0 || nCurrPosIndex < 0 ) + return false; + + const CUtlVector< Vector > &bindPos = CDmrArrayConst< Vector >( pBind->GetVertexData( nBindPosIndex ) ).Get(); + const CUtlVector< Vector > &currPos = CDmrArrayConst< Vector >( pCurr->GetVertexData( nCurrPosIndex ) ).Get(); + const CUtlVector< int > &baseTexCoordIndices = pBind->GetVertexIndexData( nBindTexIndex ); + + const int nPosCount = bindPos.Count(); + if ( nPosCount != currPos.Count() ) + return false; + + const CDmrArrayConst< Vector2D > texData( pBind->GetVertexData( nBindTexIndex ) ); + const int nBaseTexCoordCount = texData.Count(); + + FieldIndex_t nWrinkleIndex = pDelta->FindFieldIndex( CDmeVertexDeltaData::FIELD_WRINKLE ); + if ( nWrinkleIndex < 0 ) + { + nWrinkleIndex = pDelta->CreateField( CDmeVertexDeltaData::FIELD_WRINKLE ); + } + + float *pOldWrinkleData = NULL; + + if ( wrinkleOp == kAdd ) + { + // Copy the old wrinkle data + CDmAttribute *pWrinkleDeltaAttr = pDelta->GetVertexData( nWrinkleIndex ); + if ( pWrinkleDeltaAttr ) + { + CDmrArrayConst< float > wrinkleDeltaArray( pWrinkleDeltaAttr ); + if ( wrinkleDeltaArray.Count() ) + { + const CUtlVector< int > &wrinkleDeltaIndices = pDelta->GetVertexIndexData( nWrinkleIndex ); + Assert( wrinkleDeltaIndices.Count() == wrinkleDeltaArray.Count() ); + + pOldWrinkleData = reinterpret_cast< float * >( alloca( nBaseTexCoordCount * sizeof( float ) ) ); + memset( pOldWrinkleData, 0, nBaseTexCoordCount * sizeof( float ) ); + + for ( int i = 0; i < wrinkleDeltaIndices.Count(); ++i ) + { + if ( i < nPosCount ) + { + *( pOldWrinkleData + wrinkleDeltaIndices[i]) = wrinkleDeltaArray[ i ]; + } + } + } + } + } + + pDelta->RemoveAllVertexData( nWrinkleIndex ); + if ( flScale == 0.0f && wrinkleOp != kAdd ) + return true; + + float flMaxDeflection = 0.0f; + int *pWrinkleIndices = reinterpret_cast< int * >( alloca( nPosCount * sizeof( int ) ) ); + float *pWrinkleDelta = reinterpret_cast< float * >( alloca( nPosCount * sizeof( float ) ) ); + int nWrinkleCount = 0; + + float flDelta; + Vector v; + + if ( pOldWrinkleData ) + { + for ( int i = 0; i < nPosCount; ++i ) + { + v = bindPos[ i ] - currPos[ i ]; + + // Figure out the texture indices for this position index + const CUtlVector< int > &baseVerts = pBind->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, i ); + + for ( int j = 0; j < baseVerts.Count(); ++j ) + { + // See if we have a delta for this texcoord... + const int nTexCoordIndex = baseTexCoordIndices[ baseVerts[ j ] ]; + + if ( fabs( pOldWrinkleData[ nTexCoordIndex ] ) > 0.0001 || fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) ) + { + flDelta = v.Length(); + if ( flDelta > flMaxDeflection ) + { + flMaxDeflection = flDelta; + } + pWrinkleDelta[ nWrinkleCount ] = flDelta; + pWrinkleIndices[ nWrinkleCount ] = i; + ++nWrinkleCount; + break; + } + } + } + } + else + { + for ( int i = 0; i < nPosCount; ++i ) + { + v = bindPos[ i ] - currPos[ i ]; + if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) ) + { + flDelta = v.Length(); + if ( flDelta > flMaxDeflection ) + { + flMaxDeflection = flDelta; + } + pWrinkleDelta[ nWrinkleCount ] = flDelta; + pWrinkleIndices[ nWrinkleCount ] = i; + ++nWrinkleCount; + } + } + } + + if ( flMaxDeflection == 0.0f ) + return true; + + const double scaledInverseMaxDeflection = static_cast< double >( flScale ) / static_cast< double >( flMaxDeflection ); + + const int nBufSize = ( ( nBaseTexCoordCount + 7 ) >> 3 ); + unsigned char * const pUsedBits = reinterpret_cast< unsigned char* >( alloca( nBufSize * sizeof( unsigned char ) ) ); + memset( pUsedBits, 0, nBufSize ); + + for ( int i = 0; i < nWrinkleCount; ++i ) + { + float flWrinkleDelta = static_cast< float >( static_cast< double >( pWrinkleDelta[ i ] ) * scaledInverseMaxDeflection ); + + Assert( fabs( flWrinkleDelta ) <= fabs( flScale ) ); + + // NOTE: This will produce bad behavior in cases where two positions share the + // same texcoord, which shouldn't theoretically happen. + const CUtlVector< int > &baseVerts = pBind->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, pWrinkleIndices[ i ] ); + const int nBaseVertCount = baseVerts.Count(); + for ( int j = 0; j < nBaseVertCount; ++j ) + { + // See if we have a delta for this texcoord... + int nTexCoordIndex = baseTexCoordIndices[ baseVerts[j] ]; + if ( pUsedBits[ nTexCoordIndex >> 3 ] & ( 1 << ( nTexCoordIndex & 0x7 ) ) ) + continue; + + pUsedBits[ nTexCoordIndex >> 3 ] |= 1 << ( nTexCoordIndex & 0x7 ); + + if ( pOldWrinkleData ) + { + flWrinkleDelta += pOldWrinkleData[ nTexCoordIndex ]; + } + + int nDeltaIndex = pDelta->AddVertexData( nWrinkleIndex, 1 ); + pDelta->SetVertexIndices( nWrinkleIndex, nDeltaIndex, 1, &nTexCoordIndex ); + pDelta->SetVertexData( nWrinkleIndex, nDeltaIndex, 1, AT_FLOAT, &flWrinkleDelta ); + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +CDmMeshFaceIt::CDmMeshFaceIt( const CDmeMesh *pMesh, const CDmeVertexData *pVertexData /* = NULL */ ) +{ + Reset( pMesh, pVertexData ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshFaceIt::Reset( const CDmeMesh *pMesh, const CDmeVertexData *pVertexData /* = NULL */ ) +{ + m_nFaceIndex = 0; + + if ( pMesh ) + { + m_pMesh = pMesh; + m_pVertexData = pVertexData ? pVertexData : m_pVertexData ? m_pVertexData : m_pMesh->GetCurrentBaseState(); + + m_nFaceSetCount = 0; + m_nFaceSetIndex = 0; + + m_pFaceSet = NULL; + + m_nFaceSetIndexCount = 0; + m_nFaceSetIndexIndex = 0; + + m_nFaceCount = 0; + + // Get number of face sets in current mesh + m_nFaceSetCount = m_pMesh->FaceSetCount(); + if ( m_nFaceSetCount <= 0 ) + return false; + + // Get number of faces in current mesh + for ( m_nFaceSetIndex = 0; m_nFaceSetIndex < m_nFaceSetCount; ++m_nFaceSetIndex ) + { + const CDmeFaceSet *pFaceSet = m_pMesh->GetFaceSet( m_nFaceSetIndex ); + m_nFaceCount += pFaceSet->GetFaceCount(); + } + } + else if ( !m_pMesh ) + { + return false; + } + + // Set indices to point to first index of first face of first face set, accounting for + // NULL face sets and NULL faces + for ( m_nFaceSetIndex = 0; m_nFaceSetIndex < m_nFaceSetCount; ++m_nFaceSetIndex ) + { + if ( SetFaceSet() ) + return true; + } + + // All face sets were empty or full of nothing but -1's + Assert( m_nFaceSetIndex == m_nFaceSetCount ); + Assert( m_nFaceCount == 0 ); + + m_pFaceSet = NULL; + + m_nFaceSetIndexCount = 0; + m_nFaceSetIndexIndex = 0; + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CDmMeshFaceIt::Count() const +{ + return m_nFaceCount; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CDmMeshFaceIt::VertexCount() const +{ + if ( IsDone() ) + return 0; + + return m_pFaceSet->GetNextPolygonVertexCount( m_nFaceSetIndexIndex ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshFaceIt::IsDone() const +{ + if ( m_nFaceIndex < m_nFaceCount ) + { + Assert( m_nFaceSetIndex < m_nFaceSetCount ); + Assert( m_nFaceSetIndexIndex < m_nFaceSetIndexCount ); + } + else + { + Assert( m_nFaceSetIndex >= m_nFaceSetCount ); + Assert( m_nFaceSetIndexIndex >= m_nFaceSetIndexCount ); + } + + return m_nFaceIndex >= m_nFaceCount; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshFaceIt::Next() +{ + // Set indices to point to first index of first face of first face set, accounting for + // NULL face sets and NULL faces + + while ( m_nFaceSetIndex < m_nFaceSetCount ) + { + // Skip to next -1 face delimiter + while ( m_nFaceSetIndexIndex < m_nFaceSetIndexCount ) + { + if ( m_pFaceSet->GetIndex( m_nFaceSetIndexIndex ) >= 0 ) + break; + + ++m_nFaceSetIndexIndex; + } + + // Skip to next face index + while ( m_nFaceSetIndexIndex < m_nFaceSetIndexCount ) + { + if ( m_pFaceSet->GetIndex( m_nFaceSetIndexIndex ) < 0 ) + break; + + ++m_nFaceSetIndexIndex; + } + + if ( m_nFaceSetIndexIndex < m_nFaceSetIndexCount ) + { + ++m_nFaceIndex; + Assert( m_nFaceIndex < m_nFaceCount ); + return true; + } + + // Must increment the face set + ++m_nFaceSetIndex; + SetFaceSet(); + } + + // At the end of the iteration + Assert( IsDone() ); + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshFaceIt::SetFaceSet() +{ + if ( !m_pMesh ) + { + m_pFaceSet = NULL; + m_nFaceSetIndexCount = 0; + m_nFaceSetIndexIndex = 0; + + return false; + } + + if ( m_nFaceSetIndex >= m_nFaceSetCount ) + { + m_pFaceSet = NULL; + m_nFaceSetIndexCount = 0; + m_nFaceSetIndexIndex = 0; + + return false; + } + + m_pFaceSet = m_pMesh->GetFaceSet( m_nFaceSetIndex ); + m_nFaceSetIndexCount = m_pFaceSet->NumIndices(); + m_nFaceSetIndexIndex = 0; + + // Skip to the first valid face index + for ( m_nFaceSetIndexIndex = 0; m_nFaceSetIndexIndex < m_nFaceSetIndexCount; ++m_nFaceSetIndexIndex ) + { + if ( m_pFaceSet->GetIndex( m_nFaceSetIndex ) >= 0 ) + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshFaceIt::GetVertexIndices( int *pIndices, int nIndices ) const +{ + if ( IsDone() || nIndices != VertexCount() ) + { + memset( pIndices, 0, nIndices * sizeof( int ) ); + return false; + } + + int vertexIndex; + + for ( int i = m_nFaceSetIndexIndex; i < m_nFaceSetIndexCount; ++i ) + { + vertexIndex = m_pFaceSet->GetIndex( i ); + if ( vertexIndex < 0 ) + { + Assert( i == m_nFaceSetIndexIndex + VertexCount() ); + return true; + } + + Assert( i < m_nFaceSetIndexIndex + VertexCount() ); + + *pIndices = vertexIndex; + ++pIndices; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CDmMeshFaceIt::GetVertexIndices( CUtlVector< int > &vertexIndices ) const +{ + vertexIndices.SetCount( VertexCount() ); + + if ( IsDone() ) + { + memset( vertexIndices.Base(), 0, vertexIndices.Count() * sizeof( int ) ); + return false; + } + + return GetVertexIndices( vertexIndices.Base(), vertexIndices.Count() ); +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +int CDmMeshFaceIt::GetVertexIndex( int nFaceRelativeVertexIndex ) const +{ + if ( IsDone() ) + return -1; + + const int nVertexCount = VertexCount(); + if ( nVertexCount <= 0 || nFaceRelativeVertexIndex < 0 || nFaceRelativeVertexIndex >= nVertexCount ) + return -1; + + int *pVertexIndices = reinterpret_cast< int * >( alloca( nVertexCount * sizeof( int ) ) ); + if ( !GetVertexIndices( pVertexIndices, nVertexCount ) ) + return -1; + + return pVertexIndices[ nFaceRelativeVertexIndex ]; +} + + +//----------------------------------------------------------------------------- +// Copied from dmeanimationset.cpp, remove this function +// after further integrations +//----------------------------------------------------------------------------- +ControlIndex_t FindComboOpControlIndexForAnimSetControl( CDmeCombinationOperator *pComboOp, const char *pControlName, bool *pIsMulti /*= NULL*/ ) +{ + const char *pMultiControlBaseName = pControlName ? StringAfterPrefix( pControlName, "multi_" ) : NULL; + if ( pIsMulti ) + { + *pIsMulti = pMultiControlBaseName != NULL; + } + + if ( !pComboOp || !pControlName ) + return -1; + + ControlIndex_t index = pComboOp->FindControlIndex( pControlName ); + if ( index >= 0 ) + return index; + + if ( !pMultiControlBaseName ) + return -1; + + index = pComboOp->FindControlIndex( pMultiControlBaseName ); + if ( index < 0 ) + return -1; + + Assert( pComboOp->IsMultiControl( index ) ); + + return index; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmMeshUtils::CreateDeltasFromPresetGroup( + CDmePresetGroup *pPresetGroup, + CDmeCombinationOperator * pComboOp, + const CUtlVector< CUtlString > *pPurgeAllButThese, + CDmeMesh *pMesh, + CDmeVertexData *pDst, + CUtlStringMap< CUtlString > &conflictingNames, + CUtlStringMap< CDmePreset * > &presetMap ) +{ + const CDmaElementArray< CDmePreset > &presets = pPresetGroup->GetPresets(); + const int nPresetsCount = presets.Count(); + + if ( nPresetsCount <= 0 ) + return; + + for ( int i = 0; i < nPresetsCount; ++i ) + { + pComboOp->SetToBase(); + CDmePreset *pPreset = presets[ i ]; + + CDmaElementArray< CDmElement > &controlValues = pPreset->GetControlValues(); + const int nControlValues = controlValues.Count(); + for ( int j = 0; j < nControlValues; ++j ) + { + CDmElement *pControlPreset = controlValues[ j ]; + + const ControlIndex_t nControlIndex = pComboOp->FindControlIndex( pControlPreset->GetName() ); + if ( nControlIndex < 0 ) + continue; + + bool bSkip = false; + + if ( pPurgeAllButThese ) + { + for ( int k = 0; k < pPurgeAllButThese->Count(); ++k ) + { + if ( !Q_strcmp( pControlPreset->GetName(), pPurgeAllButThese->Element( k ).Get() ) ) + { + bSkip = true; + } + } + } + + if ( bSkip ) + continue; + + if ( pComboOp->IsStereoControl( nControlIndex ) ) + { + pComboOp->SetControlValue( + nControlIndex, + pControlPreset->GetValue< float >( "value", 0.0 ), + pControlPreset->GetValue< float >( "balance", 0.5 ) ); + } + else + { + pComboOp->SetControlValue( + nControlIndex, + pControlPreset->GetValue< float >( "value", 0.0 ) ); + } + + if ( pComboOp->IsMultiControl( nControlIndex ) ) + { + pComboOp->SetMultiControlLevel( + nControlIndex, + pControlPreset->GetValue< float >( "multilevel", 0.5 ) ); + } + } + + // Pass the control data from the DmeCombinationOperator into the mesh + pComboOp->Resolve(); + pComboOp->Operate(); + + pMesh->Resolve(); + pMesh->SetBaseStateToDeltas( pDst ); + + CUtlString presetName = pPreset->GetName(); + + // Look for any conflicting pre-existing names + for ( int presetSuffix = 1; pComboOp->FindControlIndex( presetName ) >= 0 || pMesh->FindDeltaState( presetName ) != NULL || conflictingNames.Defined( presetName ) || presetMap.Defined( presetName ); ++presetSuffix ) + { + presetName = pPreset->GetName(); + presetName += presetSuffix; + } + + if ( Q_strcmp( pPreset->GetName(), presetName ) ) + { + // Had to rename preset... save name for later renaming back + conflictingNames[ presetName ] = pPreset->GetName(); + } + + presetMap[ presetName ] = pPreset; + + pMesh->ModifyOrCreateDeltaStateFromBaseState( presetName, pDst, true ); + } +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +void CDmMeshUtils::PurgeUnreferencedDeltas( CDmeMesh *pMesh, CUtlStringMap< CDmePreset * > &presetMap, const CUtlVector< CUtlString > *pPurgeAllButThese, CDmeCombinationOperator *pComboOp ) +{ + // Loop because deleting changes indexing + bool bDeleted = false; + do + { + bDeleted = false; + for ( int i = 0; i < pMesh->DeltaStateCount(); ++i ) + { + const char *pDeltaStateName = pMesh->GetDeltaState( i )->GetName(); + + if ( presetMap.Defined( pDeltaStateName ) ) + continue; + + bool bDelete = true; + + if ( pPurgeAllButThese ) + { + for ( int j = 0; j < pPurgeAllButThese->Count(); ++j ) + { + if ( !Q_strcmp( pDeltaStateName, pPurgeAllButThese->Element( j ).Get() ) ) + { + bDelete = false; + break; + } + + const ControlIndex_t nControlIndex = pComboOp->FindControlIndex( pPurgeAllButThese->Element( j ) ); + if ( nControlIndex < 0 ) + continue; + + for ( int k = 0; k < pComboOp->GetRawControlCount( nControlIndex ); ++k ) + { + if ( !Q_strcmp( pDeltaStateName, pComboOp->GetRawControlName( nControlIndex, k ) ) ) + { + bDelete = false; + break; + } + } + } + } + + if ( bDelete ) + { + pMesh->DeleteDeltaState( pDeltaStateName ); + bDeleted = true; + break; + } + } + } while( bDeleted ); + + // Loop because deleting changes indexing + do + { + bDeleted = false; + for ( int i = 0; i < pComboOp->GetControlCount(); ++i ) + { + const char *pControlName = pComboOp->GetControlName( i ); + + if ( presetMap.Defined( pControlName ) ) + continue; + + bool bDelete = true; + + if ( pPurgeAllButThese ) + { + for ( int j = 0; j < pPurgeAllButThese->Count(); ++j ) + { + if ( !Q_strcmp( pControlName, pPurgeAllButThese->Element( j ) ) ) + { + bDelete = false; + break; + } + } + } + + if ( bDelete ) + { + pComboOp->RemoveControl( pControlName ); + bDeleted = true; + break; + } + } + } while( bDeleted ); + + // Rename any that can be renamed... which should be all of them + for ( int i = 0; i < presetMap.GetNumStrings(); ++i ) + { + const char *pPresetName = presetMap.String( i ); + CDmePreset *pPreset = presetMap[ i ]; + + if ( Q_strcmp( pPreset->GetName(), pPresetName ) ) + { + const ControlIndex_t nOrigIndex = pComboOp->FindControlIndex( pPreset->GetName() ); + const ControlIndex_t nRenamedIndex = pComboOp->FindControlIndex( pPresetName ); + CDmeVertexDeltaData *pOrigDelta = pMesh->FindDeltaState( pPreset->GetName() ); + CDmeVertexDeltaData *pRenamedDelta = pMesh->FindDeltaState( pPresetName ); + + if ( nOrigIndex < 0 && nRenamedIndex >= 0 && pOrigDelta == NULL && pRenamedDelta != NULL ) + { + pComboOp->RemoveControl( pPresetName ); + pRenamedDelta->SetName( pPreset->GetName() ); + } + } + } +} |