csgo-2018-source/dmeutils/dmmeshutils.cpp
2021-07-24 21:11:47 -07:00

3933 lines
111 KiB
C++

//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. =======
//
// Functions which do things to a DmeMesh
//
//=============================================================================
// Valve includes
#include "dmeutils/dmmeshutils.h"
#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 "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"
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
DEFINE_LOGGING_CHANNEL_NO_TAGS( LOG_MESHUTILS, "MeshUtils" );
#ifndef LOG_COLOR_RED
#define LOG_COLOR_RED Color( 255, 0, 0, 255 )
#endif // #ifndef LOG_COLOR_RED
#ifndef LOG_COLOR_YELLOW
#define LOG_COLOR_YELLOW Color( 255, 255, 0, 255 )
#endif // #ifndef LOG_COLOR_YELLOW
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
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 )
{
CDmeVertexData *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 &origData( origData[ origIndices[ i ] ] );
mirrorData = origData;
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() );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static void MergeJointData(
CDmeVertexData *pDstBase,
CDmeVertexData *pSrcBase,
const CUtlMap< int, int > &jointMap )
{
if ( !pSrcBase || !pDstBase )
return;
const int nSrcPositionIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
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 );
if ( nSrcPositionIndex < 0 || nSrcJointWeightsIndex < 0 || nSrcJointIndicesIndex < 0 || nDstJointWeightsIndex < 0 || nDstJointIndicesIndex < 0 )
return;
const int nSrcJointCount = pSrcBase->JointCount();
const int nDstJointCount = pDstBase->JointCount();
if ( nSrcJointCount <= 0 || nDstJointCount <= 0 )
return;
const int nSrcPosCount = CDmrGenericArray( pSrcBase->GetVertexData( nSrcPositionIndex ) ).Count();
const CDmrArrayConst< float > srcWeights( pSrcBase->GetVertexData( nSrcJointWeightsIndex ) );
const CDmrArrayConst< int > srcIndices( pSrcBase->GetVertexData( nSrcJointIndicesIndex ) );
CDmrArray< float > dstWeights( pDstBase->GetVertexData( nDstJointWeightsIndex ) );
CDmrArray< int > dstIndices( pDstBase->GetVertexData( nDstJointIndicesIndex ) );
int nDstIndex = dstWeights.Count();
int nSrcIndex = 0;
dstWeights.AddMultipleToTail( nSrcPosCount * nDstJointCount );
dstIndices.AddMultipleToTail( nSrcPosCount * nDstJointCount );
while ( nDstIndex < dstWeights.Count() )
{
float flTotalWeight = 0.0f;
for ( int i = 0; i < nDstJointCount; ++i )
{
if ( i < nSrcJointCount )
{
Assert( nSrcIndex < srcWeights.Count() );
const CUtlMap< int, int >::IndexType_t nJointMapIndex = jointMap.Find( srcIndices[ nSrcIndex ] );
if ( jointMap.IsValidIndex( nJointMapIndex ) )
{
dstWeights.Set( nDstIndex, srcWeights[ nSrcIndex ] );
dstIndices.Set( nDstIndex, jointMap.Element( nJointMapIndex ) );
flTotalWeight += dstWeights[ nDstIndex ];
}
else
{
Warning( "Can't Joint Index %d On Src Isn't Mapped To Dst\n", srcIndices[ nSrcIndex ] );
dstWeights.Set( nDstIndex, 0.0f );
dstIndices.Set( nDstIndex, -1 );
}
++nSrcIndex;
}
else
{
dstWeights.Set( nDstIndex, 0.0f );
dstIndices.Set( nDstIndex, -1 );
}
++nDstIndex;
}
// Renormalize if weight didn't equal 1
if ( flTotalWeight > 0.0f && FloatMakePositive( flTotalWeight - 1.0f ) > FLT_EPSILON * 100.0 )
{
for ( int i = nDstIndex - nDstJointCount; i < nDstIndex; ++i )
{
dstWeights.Set( i, dstWeights.Element( i ) / flTotalWeight );
}
}
}
}
//-----------------------------------------------------------------------------
// 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,
CUtlMap< int, int > *pSkinMap /* = NULL */ )
{
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 ( pSkinMap && nSrcJointWeightsIndex >= 0 && nSrcJointIndicesIndex >= 0 )
{
MergeJointData( pDstBase, pSrcBase, *pSkinMap );
}
else
{
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 ( V_strcmp( pSrcBase->FieldName( i ), pDstBase->FieldName( j ) ) )
continue;
bMerged = true;
if ( i == nSrcJointWeightsIndex || i == nSrcJointIndicesIndex )
continue;
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 );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDmMeshUtils::Merge( CDmeMesh *pSrcMesh, CDmeMesh *pDstMesh, int nSkinningJointIndex, CUtlMap< int, int > *pJointMap /* = NULL */ )
{
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;
pSrcDag->GetAbsTransform( nMat );
matrix3x4_t pMat;
pDstDag->GetAbsTransform( 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, pJointMap );
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;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline bool IsEqual( const float &flA, const float &flB, float flTolerance )
{
return ( FloatMakePositive( flA - flB ) <= flTolerance );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline bool IsEqual( const Vector &vA, const Vector &vB, float flTolerance )
{
return VectorsAreEqual( vA, vB, flTolerance );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline bool IsEqual( const Vector2D &vA, const Vector2D &vB, float flTolerance )
{
if ( FloatMakePositive( vA.x - vB.x ) > flTolerance )
return false;
return ( FloatMakePositive( vA.y - vB.y ) <= flTolerance );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
template < class T >
bool MergeField(
CDmeVertexData *pDstVertexData,
FieldIndex_t nDstFieldIndex,
CDmAttribute *pDstAttr,
CDmeVertexData *pSrcVertexData,
FieldIndex_t nSrcFieldIndex,
CDmAttribute *pSrcAttr,
int nIndexOffset,
float flTolerance )
{
CUtlMap< int, int > srcToDstMap( CDefOps< int >::LessFunc );
if ( !pDstVertexData || !pDstAttr || !pSrcAttr )
return false;
const CDmrArrayConst< T > dstData( pDstAttr );
const CDmrArrayConst< T > srcData( pSrcAttr );
if ( dstData.Count() <= 0 || srcData.Count() <= 0 )
return false;
CUtlVector< T > newDstData;
for ( int i = 0; i < srcData.Count(); ++i )
{
int nDstIndex = -1;
const T &srcVal = srcData[i];
for ( int j = 0; j < dstData.Count(); ++j )
{
const T &dstVal = dstData[j];
if ( IsEqual( srcVal, dstVal, flTolerance ) )
{
nDstIndex = j;
break;
}
}
if ( nDstIndex < 0 )
{
nDstIndex = dstData.Count() + newDstData.AddToTail( srcVal );
}
Assert( nDstIndex >= 0 );
srcToDstMap.InsertOrReplace( i, nDstIndex );
}
CUtlVector< int > newIndices;
const CUtlVector< int > &srcIndexData = pSrcVertexData->GetVertexIndexData( nSrcFieldIndex );
Assert( srcIndexData.Count() > 0 );
for ( int i = 0; i < srcIndexData.Count(); ++i )
{
const CUtlMap< int, int >::IndexType_t nFindIndex = srcToDstMap.Find( srcIndexData[i] );
Assert( srcToDstMap.IsValidIndex( nFindIndex ) );
newIndices.AddToTail( srcToDstMap.Element( nFindIndex ) );
}
Assert( pDstVertexData->VertexCount() == nIndexOffset + newIndices.Count() );
pDstVertexData->SetVertexIndices( nDstFieldIndex, nIndexOffset, newIndices.Count(), newIndices.Base() );
if ( newDstData.Count() > 0 )
{
const int nStart = dstData.Count();
pDstVertexData->AddVertexData( nDstFieldIndex, newDstData.Count() );
pDstVertexData->SetVertexData( nDstFieldIndex, nStart, newDstData.Count(), ArrayTypeToValueType( pDstAttr->GetType() ), newDstData.Base() );
}
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;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDmMeshUtils::NewMerge(
CDmeMesh *pDstMesh,
CDmeMesh *pSrcMesh,
const CUtlVector< CDmMeshComp::CEdge * > &edgeList,
float flTolerance,
float flTexCoordTolerance )
{
if ( !pSrcMesh || !pDstMesh )
return false;
CDmeVertexData *pSrcData = pSrcMesh->FindBaseState( "bind" );
CDmeVertexData *pDstData = pDstMesh->FindBaseState( "bind" );
if ( !pSrcData || !pDstData )
return false;
int nDstIndexCount = pDstData->VertexCount();
int nDstIndexOffset = nDstIndexCount;
if ( nDstIndexCount <= 0 )
return false;
// Merge or add face sets
for ( int i = 0; i < pSrcMesh->FaceSetCount(); ++i )
{
CDmeFaceSet *pSrcFaceSet = pSrcMesh->GetFaceSet( i );
if ( !pSrcFaceSet )
continue;
const int nSrcIndexCount = pSrcFaceSet->GetIndexCount();
if ( nSrcIndexCount <= 0 )
continue;
CDmeMaterial *pSrcMat = pSrcFaceSet->GetMaterial();
const char *pszSrcMatName = pSrcMat ? pSrcMat->GetMaterialName() : NULL;
bool bCopy = true;
if ( pszSrcMatName )
{
for ( int j = 0; j < pDstMesh->FaceSetCount(); ++j )
{
CDmeFaceSet *pDstFaceSet = pDstMesh->GetFaceSet( j );
if ( !pDstFaceSet )
continue;
CDmeMaterial *pDstMat = pDstFaceSet->GetMaterial();
if ( !pDstMat )
continue;
if ( !V_strcmp( pDstMat->GetMaterialName(), pszSrcMatName ) )
{
bCopy = false;
// Merge
pDstData->AddVertexIndices( nSrcIndexCount );
for ( int k = 0; k < pSrcFaceSet->NumIndices(); ++k )
{
const int nFaceSetIndex = pSrcFaceSet->GetIndex( k );
const int nToSetIndex = pDstFaceSet->NumIndices();
pDstFaceSet->AddIndices( 1 );
if ( nFaceSetIndex < 0 )
{
pDstFaceSet->SetIndex( nToSetIndex, -1 );
}
else
{
pDstFaceSet->SetIndex( nToSetIndex, nFaceSetIndex + nDstIndexCount );
}
}
nDstIndexCount += nSrcIndexCount;
break;
}
}
}
if ( bCopy )
{
CDmeFaceSet *pDmeFaceSet = pSrcMesh->GetFaceSet( i )->Copy();
pDmeFaceSet->SetFileId( pDstMesh->GetFileId(), TD_DEEP );
pDstData->AddVertexIndices( nSrcIndexCount );
for ( int j = 0; j < pDmeFaceSet->NumIndices(); ++j )
{
int nFaceSetIndex = pDmeFaceSet->GetIndex( j );
if ( nFaceSetIndex >= 0 )
{
pDmeFaceSet->SetIndex( j, nFaceSetIndex + nDstIndexCount );
}
}
nDstIndexCount += nSrcIndexCount;
pDstMesh->AddFaceSet( pDmeFaceSet );
}
}
for ( int nSrcFieldIndex = 0; nSrcFieldIndex < pSrcData->FieldCount(); ++nSrcFieldIndex )
{
CDmAttribute *pSrcAttr = pSrcData->GetVertexData( nSrcFieldIndex );
const char *pszSrcFieldName = pSrcData->FieldName( nSrcFieldIndex );
if ( !pSrcAttr || !pszSrcFieldName || V_strlen( pszSrcFieldName ) <= 0 )
continue;
const FieldIndex_t nDstFieldIndex = pDstData->FindFieldIndex( pszSrcFieldName );
if ( nDstFieldIndex < 0 )
{
Log_Error( LOG_MESHUTILS, LOG_COLOR_RED, "ERROR: Merge( \"%s\", \"%s\" ): Cannot Find Field \"%s\" On Dst Mesh: \"%s\"\n",
pDstMesh->GetName(), pSrcMesh->GetName(), pszSrcFieldName, pDstMesh->GetName() );
continue;
}
CDmAttribute *pDstAttr = pDstData->GetVertexData( nDstFieldIndex );
if ( !pDstAttr )
{
Log_Error( LOG_MESHUTILS, LOG_COLOR_RED, "ERROR: Merge( \"%s\", \"%s\" ): Cannot Find Field \"%s\" On Dst Mesh: \"%s\"\n",
pDstMesh->GetName(), pSrcMesh->GetName(), pszSrcFieldName, pDstMesh->GetName() ); continue;
}
if ( pSrcAttr->GetType() != pDstAttr->GetType() )
{
Log_Error( LOG_MESHUTILS, LOG_COLOR_RED, "ERROR: Merge( \"%s\", \"%s\" ): Field \"%s\" Type Mismatch: Src %s vs Dst %s\n",
pDstMesh->GetName(), pSrcMesh->GetName(), pszSrcFieldName, pSrcAttr->GetTypeString(), pDstAttr->GetTypeString() );
continue;
}
bool bOk = false;
const DmAttributeType_t nAttrType = pSrcAttr->GetType();
if ( nAttrType == AT_FLOAT_ARRAY )
{
bOk = MergeField< float >( pDstData, nDstFieldIndex, pDstAttr, pSrcData, nSrcFieldIndex, pSrcAttr, nDstIndexOffset, flTolerance );
}
else if ( nAttrType == AT_VECTOR2_ARRAY )
{
bOk = MergeField< Vector2D >( pDstData, nDstFieldIndex, pDstAttr, pSrcData, nSrcFieldIndex, pSrcAttr, nDstIndexOffset, flTexCoordTolerance );
}
else if ( nAttrType == AT_VECTOR3_ARRAY )
{
bOk = MergeField< Vector >( pDstData, nDstFieldIndex, pDstAttr, pSrcData, nSrcFieldIndex, pSrcAttr, nDstIndexOffset, flTolerance );
}
if ( !bOk )
{
Log_Warning( LOG_MESHUTILS, LOG_COLOR_YELLOW, "WARNING: Merge( \"%s\", \"%s\" ): Field \"%s\" Not Merged\n",
pDstMesh->GetName(), pSrcMesh->GetName(), pszSrcFieldName );
}
}
pDstData->Resolve();
pDstMesh->Resolve();
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 ];
Assert( !pPreset->IsAnimated() ); // deal with this after GDC
if ( pPreset->IsAnimated() )
continue;
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 );
// 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( "leftValue", 1.0f );
pDstControlValue->SetValue( "rightValue", 1.0f );
}
else
{
pDstControlValue->SetValue( "value", 1.0f );
}
if ( bMulti )
{
CFmtStr multiControlName( "%s_multi", pControlName );
CDmElement *pDstMultiControlValue = pDstPreset->FindOrAddControlValue( multiControlName );
pDstMultiControlValue->SetValue( "value", 0.5f );
}
continue;
}
for ( int k = 0; k < nControlValueCount; ++k )
{
CDmElement *pControlPreset = controlValues[ k ];
if ( !pControlPreset )
continue;
if ( !Q_strcmp( pControlName, pControlPreset->GetName() ) )
{
pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName );
if ( bStereo )
{
pDstControlValue->SetValue( "leftValue", pControlPreset->GetValue( "leftValue", 0.0f ) );
pDstControlValue->SetValue( "rightValue", pControlPreset->GetValue( "rightValue", 0.0f ) );
}
else
{
pDstControlValue->SetValue( "value", pControlPreset->GetValue( "value", 0.0f ) );
}
if ( bMulti )
{
CFmtStr multiControlName( "%s_multi", pControlName );
CDmElement *pMultiControlPreset = pPreset->FindControlValue( multiControlName );
CDmElement *pDstMultiControlValue = pDstPreset->FindOrAddControlValue( multiControlName );
pDstMultiControlValue->SetValue( "value", pMultiControlPreset ? pMultiControlPreset->GetValue( "value", 0.5f ) : 0.5f );
}
break;
}
}
if ( !pDstControlValue )
{
pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName );
if ( bStereo )
{
float flValue = pComboOp->GetControlDefaultValue( j );
pDstControlValue->SetValue( "leftValue", flValue );
pDstControlValue->SetValue( "rightValue", flValue );
}
else
{
pDstControlValue->SetValue( "value", pComboOp->GetControlDefaultValue( j ) );
}
if ( bMulti )
{
CFmtStr multiControlName( "%s_multi", pControlName );
CDmElement *pDstMultiControlValue = pDstPreset->FindOrAddControlValue( multiControlName );
pDstMultiControlValue->SetValue( "value", 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 */,
bool bUseNormalForSign /* = false */ )
{
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 ) );
int nWrinkleIndex;
for ( int i = 0; i < wrinkleDeltaIndices.Count(); ++i )
{
nWrinkleIndex = wrinkleDeltaIndices[ i ];
if ( i < nPosCount )
{
*( pOldWrinkleData + nWrinkleIndex ) = 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;
const int *pNormalIndices = NULL;
const Vector *pNormals = NULL;
if ( bUseNormalForSign )
{
const CUtlVector<int> &normalIndices = pBind->GetVertexIndexData( CDmeVertexData::FIELD_NORMAL );
const CUtlVector< Vector > &normals = pBind->GetNormalData();
if ( normalIndices.Count() > 0 && normals.Count() > 0 )
{
pNormalIndices = normalIndices.Base();
pNormals = normals.Base();
}
else
{
Warning( "ComputeNormalWrinkle called but no normals on Mesh: %s\n", pDeltaMesh->GetName() );
bUseNormalForSign = false;
}
}
if ( pOldWrinkleData || bUseNormalForSign )
{
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 ( ( pOldWrinkleData && 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.NormalizeInPlace();
if ( flDelta > flMaxDeflection )
{
flMaxDeflection = flDelta;
}
if ( bUseNormalForSign )
{
Vector vNormal = pNormals[ pNormalIndices[ baseVerts[ j ] ] ];
vNormal.NormalizeInPlace();
if ( DotProduct( v, vNormal ) < 0 )
{
flDelta = -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 ];
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
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 ];
Assert( !pPreset->IsAnimated() ); // deal with this after GDC
if ( pPreset->IsAnimated() )
continue;
CDmaElementArray< CDmElement > &controlValues = pPreset->GetControlValues();
const int nControlValues = controlValues.Count();
for ( int j = 0; j < nControlValues; ++j )
{
CDmElement *pControlPreset = controlValues[ j ];
bool bIsMulti;
const ControlIndex_t nControlIndex = FindComboOpControlIndexForAnimSetControl( pComboOp, pControlPreset->GetName(), &bIsMulti );
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>( "leftValue", 0.0f ),
pControlPreset->GetValue<float>( "rightValue", 0.0f ) );
}
else
{
pComboOp->SetControlValue(
nControlIndex,
pControlPreset->GetValue< float >( "value", 0.0 ) );
}
if ( bIsMulti )
{
pComboOp->SetMultiControlLevel(
nControlIndex,
pControlPreset->GetValue< float >( "value", 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() );
}
}
}
}
//-----------------------------------------------------------------------------
// Returns the first DmeModel which is an ancestor of the specified DmeMesh
//-----------------------------------------------------------------------------
CDmeModel *CDmMeshUtils::GetDmeModelFromMesh( CDmeMesh *pDmeMesh )
{
if ( !pDmeMesh )
return NULL;
CUtlStack< CDmeDag * > parentWalk;
CDmeDag *pDmeDag;
for ( int i = pDmeMesh->GetParentCount() - 1; i >= 0; --i )
{
pDmeDag = pDmeMesh->GetParent( i );
if ( !pDmeDag )
continue;
parentWalk.Push( pDmeDag );
}
while ( parentWalk.Count() )
{
parentWalk.Pop( pDmeDag );
if ( !pDmeDag )
continue;
if ( CastElement< CDmeModel >( pDmeDag ) )
return CastElement< CDmeModel >( pDmeDag );
parentWalk.Push( pDmeDag->GetParent() );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Does a depth first walk of the DmeDag hierarchy specified until a DmeDag with the
// requested name is found, returns that dag if found, NULL if not found or on error
//-----------------------------------------------------------------------------
CDmeDag *CDmMeshUtils::FindDagByNameInHierarchy( CDmeDag *pSearchDag, const char *pszSearchName )
{
if ( !pSearchDag || !pszSearchName || V_strlen( pszSearchName ) <= 0 )
return NULL;
CUtlStack< CDmeDag * > depthFirstStack;
depthFirstStack.Push( pSearchDag );
CDmeDag *pDmeDag;
while ( depthFirstStack.Count() > 0 )
{
depthFirstStack.Pop( pDmeDag );
if ( !pDmeDag )
continue;
if ( !V_strcmp( pDmeDag->GetName(), pszSearchName ) )
return pDmeDag;
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
{
depthFirstStack.Push( pDmeDag->GetChild( i ) );
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Adds the joint to the specified model by finding the common ancestor
// Duplicates the required hierarchy of the source joint
//-----------------------------------------------------------------------------
CDmeDag *CDmMeshUtils::AddJointToModel( CDmeModel *pDstModel, CDmeDag *pSrcJoint )
{
if ( !pDstModel || !pSrcJoint || CastElement< CDmeModel >( pSrcJoint ) )
return NULL;
// See if the dag is already part of the DmeModel
if ( pDstModel->GetJointIndex( pSrcJoint ) >= 0 )
return pSrcJoint;
// Find it by name in the DmeModel hierarchy
CDmeDag *pDstDag = FindDagByNameInHierarchy( pDstModel, pSrcJoint->GetName() );
if ( pDstDag )
{
// Same named joint was already in hierarchy just not in model
// TODO: Warn if joint is not equal to pSrcJoint
pDstModel->AddJoint( pDstDag );
// TODO: This is a big hammer, make 'AddJoint' add the base state for just the added joint
pDstModel->CaptureJointsToBaseState( "bind" );
pDstModel->Resolve();
return pDstDag;
}
// Walk backwards until we find an ancestor of pSrcJoint that exists in the DstModel
// or until we get to the DmeModel ancestor of the pSrcJoint
CDmeDag *pDstParent = pDstModel;
CUtlStack< CDmeDag * > dupeList;
dupeList.Push( pSrcJoint );
for ( CDmeDag *pSrcParent = pSrcJoint->GetParent(); pSrcParent; pSrcParent = pSrcParent->GetParent() )
{
// If we've gotten to a CDmeModel then leave the pDstParent as the pDstModel
if ( CastElement< CDmeModel >( pSrcParent ) )
break;
CDmeDag *pTmpDag = FindDagByNameInHierarchy( pDstModel, pSrcParent->GetName() );
if ( pTmpDag )
{
pDstParent = pTmpDag;
break;
}
dupeList.Push( pSrcParent );
}
CDmeDag *pSrcDag = NULL;
matrix3x4a_t mAbs;
while ( dupeList.Count() )
{
dupeList.Pop( pSrcDag );
if ( !pSrcDag )
{
Error( "NULL DmeDag in AddJoint\n" );
break;
}
CDmeDag *pDstDag = pDstModel->AddJoint( pSrcDag->GetName(), pDstParent );
if ( !pDstDag )
{
Error( "Couldn't AddJoint( %s )\n", pSrcDag->GetName() );
break;
}
// Only settings the Abs transform... not copyting any other dynamic attributes
pSrcDag->GetAbsTransform( mAbs );
pDstDag->SetAbsTransform( mAbs );
pDstParent = pDstDag;
}
pDstModel->CaptureJointsToBaseState( "bind" );
return pDstParent;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CDmMeshUtils::MergeMeshAndSkeleton( CDmeMesh *pDstMesh, CDmeMesh *pSrcMesh )
{
if ( !pDstMesh || !pSrcMesh )
return false;
CDmeModel *pDstModel = GetDmeModelFromMesh( pDstMesh );
CDmeModel *pSrcModel = GetDmeModelFromMesh( pSrcMesh );
CDmeVertexData *pSrcBind = pSrcMesh->GetBindBaseState();
if ( !pDstModel || !pSrcModel || !pSrcBind || !pSrcBind->HasSkinningData() )
return false;
CUtlMap< int, int > jointRemap( CDefOps< int >::LessFunc );
const FieldIndex_t nSrcJointIndex = pSrcBind->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES );
if ( nSrcJointIndex < 0 )
{
Warning( "No Joints!\n" );
return false;
}
const CDmrArrayConst< int > srcJointIndices = pSrcBind->GetVertexData( nSrcJointIndex );
for ( int i = 0; i < srcJointIndices.Count(); ++i )
{
const int nSrcIndex = srcJointIndices[i];
if ( jointRemap.IsValidIndex( jointRemap.Find( nSrcIndex ) ) )
continue;
CDmeDag *pSrcJoint = pSrcModel->GetJoint( nSrcIndex );
if ( !pSrcJoint )
continue;
CDmeDag *pDstJoint = pDstModel->GetJoint( pSrcJoint->GetName() );
if ( !pDstJoint )
{
pDstJoint = AddJointToModel( pDstModel, pSrcJoint );
}
if ( pDstJoint )
{
const int nDstIndex = pDstModel->GetJointIndex( pDstJoint );
Assert( nDstIndex >= 0 );
jointRemap.InsertOrReplace( nSrcIndex, nDstIndex );
continue;
}
else
{
Error( "Can't Add Joint: %s\n", pSrcJoint->GetName() );
return false;
}
}
Merge( pSrcMesh, pDstMesh, -1, &jointRemap );
// If there are any other base states on the mesh, just copy the bind base state overtop
CDmeVertexData *pBindState = pDstMesh->GetBindBaseState();
if ( pBindState )
{
const int nBaseStateCount = pDstMesh->BaseStateCount();
for ( int i = 0; i < nBaseStateCount; ++i )
{
CDmeVertexData *pBaseState = pDstMesh->GetBaseState( i );
if ( pBindState != pBaseState )
{
pBindState->CopyTo( pBaseState );
}
}
}
return true;
}