You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3313 lines
93 KiB
3313 lines
93 KiB
//========= 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() ); |
|
} |
|
} |
|
} |
|
}
|
|
|