This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

View File

@ -0,0 +1,275 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// A class representing session state for the SFM
//
//=============================================================================
#include "sfmobjects/exportfacialanimation.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmeanimationset.h"
#include "movieobjects/dmegamemodel.h"
#include "movieobjects/dmetrackgroup.h"
#include "movieobjects/dmetrack.h"
#include "movieobjects/dmesound.h"
#include "movieobjects/dmelog.h"
#include "movieobjects/dmechannel.h"
//-----------------------------------------------------------------------------
// Contains export information
//-----------------------------------------------------------------------------
struct ExportInfo_t
{
CDmeFilmClip *m_pMovie;
CDmeFilmClip *m_pShot;
CDmeAnimationSet *m_pAnimationSet;
DmeTime_t m_tExportStart;
DmeTime_t m_tExportEnd;
};
//-----------------------------------------------------------------------------
// Used to transform channel data into export time
//-----------------------------------------------------------------------------
static void ComputeExportChannelScaleBias( double *pScale, DmeTime_t *pBias, ExportInfo_t &info, CDmeChannel *pChannel )
{
DmeClipStack_t channelToGlobal;
if ( pChannel->BuildClipStack( &channelToGlobal, info.m_pMovie, info.m_pShot ) )
{
DmeTime_t tOffset = CDmeClip::FromChildMediaTime( channelToGlobal, DMETIME_ZERO, false );
DmeTime_t tScale = CDmeClip::FromChildMediaTime( channelToGlobal, DmeTime_t( 1.0f ), false );
*pBias = tOffset - info.m_pShot->GetStartTime();
*pScale = ( tScale - tOffset ).GetSeconds();
}
}
static void GetExportTimeRange( DmeTime_t *pExportStart, DmeTime_t *pExportEnd, CDmeFilmClip *pShot )
{
*pExportStart = DMETIME_ZERO;
*pExportEnd = pShot->GetDuration();
}
//-----------------------------------------------------------------------------
// Adds a log layer to the list of logs for export
//-----------------------------------------------------------------------------
static void AddLogLayerForExport( ExportInfo_t &info, CDmElement *pRoot, const char *pControlName, CDmeChannel *pChannel )
{
CDmeLog *pLog = pChannel->GetLog();
if ( !pLog || pLog->GetNumLayers() == 0 )
return;
CDmrElementArray<> animations( pRoot, "animations" );
DmeTime_t tBias;
double flScale;
ComputeExportChannelScaleBias( &flScale, &tBias, info, pChannel );
// Only export the base layer
CDmeLogLayer* pLogLayer = pLog->GetLayer( 0 )->Copy();
pLogLayer->SetName( pControlName );
pLogLayer->ScaleBiasKeyTimes( flScale, tBias );
// Forcibly add keys @ the start + end time
DmeTime_t tStartTime = ( info.m_tExportStart - tBias ) / flScale;
DmeTime_t tEndTime = ( info.m_tExportEnd - tBias ) / flScale;
pLogLayer->InsertKeyFromLayer( info.m_tExportStart, pLog->GetLayer(0), tStartTime );
pLogLayer->InsertKeyFromLayer( info.m_tExportEnd, pLog->GetLayer(0), tEndTime );
pLogLayer->RemoveKeysOutsideRange( info.m_tExportStart, info.m_tExportEnd );
animations.AddToTail( pLogLayer );
}
//-----------------------------------------------------------------------------
// Exports animations
//-----------------------------------------------------------------------------
static void ExportAnimations( ExportInfo_t &info, CDmElement *pRoot )
{
CDmrElementArray<> animations( pRoot, "animations", true );
// Build a list of all controls
const CDmaElementArray< CDmElement > &controls = info.m_pAnimationSet->GetControls();
int nControlCount = controls.Count();
for ( int i = 0; i < nControlCount; ++i )
{
CDmElement *pControl = controls[i];
if ( !pControl || pControl->GetValue<bool>( "transform" ) )
continue;
bool bIsStereo = pControl->GetValue<bool>( "combo" );
if ( bIsStereo )
{
CDmeChannel *pValueChannel = pControl->GetValueElement<CDmeChannel>( "valuechannel" );
CDmeChannel *pBalanceChannel = pControl->GetValueElement<CDmeChannel>( "balancechannel" );
CDmeLog *pValueLog = pValueChannel->GetLog();
CDmeLog *pBalanceLog = pBalanceChannel->GetLog();
if ( pValueLog && pBalanceLog && pValueLog->GetNumLayers() != 0 && pBalanceLog->GetNumLayers() != 0 )
{
DmeTime_t tValueBias, tBalanceBias;
double flValueScale, flBalanceScale;
ComputeExportChannelScaleBias( &flValueScale, &tValueBias, info, pValueChannel );
ComputeExportChannelScaleBias( &flBalanceScale, &tBalanceBias, info, pBalanceChannel );
// Make copy to maintain log layer types
CDmeLogLayer *pValueLogLayer = pValueLog->GetLayer( 0 )->Copy();
CDmeLogLayer *pBalanceLogLayer = pBalanceLog->GetLayer( 0 )->Copy();
pValueLogLayer->ScaleBiasKeyTimes( flValueScale, tValueBias );
pBalanceLogLayer->ScaleBiasKeyTimes( flBalanceScale, tBalanceBias );
// Forcibly insert keys @ start + end times.
DmeTime_t tStartTime = ( info.m_tExportStart - tValueBias ) / flValueScale;
DmeTime_t tEndTime = ( info.m_tExportEnd - tValueBias ) / flValueScale;
pValueLogLayer->InsertKeyFromLayer( info.m_tExportStart, pValueLog->GetLayer(0), tStartTime );
pValueLogLayer->InsertKeyFromLayer( info.m_tExportEnd, pValueLog->GetLayer(0), tEndTime );
tStartTime = ( info.m_tExportStart - tBalanceBias ) / flBalanceScale;
tEndTime = ( info.m_tExportEnd - tBalanceBias ) / flBalanceScale;
pBalanceLogLayer->InsertKeyFromLayer( info.m_tExportStart, pBalanceLog->GetLayer(0), tStartTime );
pBalanceLogLayer->InsertKeyFromLayer( info.m_tExportEnd, pBalanceLog->GetLayer(0), tEndTime );
pValueLogLayer->RemoveKeysOutsideRange( info.m_tExportStart, info.m_tExportEnd );
pBalanceLogLayer->RemoveKeysOutsideRange( info.m_tExportStart, info.m_tExportEnd );
char pControlName[512];
Q_snprintf( pControlName, sizeof(pControlName), "value_%s", pControl->GetName() );
pValueLogLayer->SetName( pControlName );
animations.AddToTail( pValueLogLayer );
Q_snprintf( pControlName, sizeof(pControlName), "balance_%s", pControl->GetName() );
pBalanceLogLayer->SetName( pControlName );
animations.AddToTail( pBalanceLogLayer );
}
}
else
{
CDmeChannel *pChannel = pControl->GetValueElement<CDmeChannel>( "channel" );
AddLogLayerForExport( info, pRoot, pControl->GetName(), pChannel );
}
if ( pControl->GetValue<bool>( "multi" ) )
{
char pControlName[512];
Q_snprintf( pControlName, sizeof(pControlName), "multi_%s", pControl->GetName() );
CDmeChannel *pMultiChannel = pControl->GetValueElement<CDmeChannel>( "multilevelchannel" );
AddLogLayerForExport( info, pRoot, pControlName, pMultiChannel );
}
}
}
//-----------------------------------------------------------------------------
// Helper to export sounds
//-----------------------------------------------------------------------------
static void ExportSounds( ExportInfo_t &info, CDmElement *pRoot, CDmeClip *pClip, DmeTime_t tOffset )
{
CDmrElementArray<> sounds( pRoot, "sounds", true );
DmeClipStack_t soundToGlobal;
int gc = pClip->GetTrackGroupCount();
for ( int i = 0; i < gc; ++i )
{
CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
DMETRACKGROUP_FOREACH_CLIP_TYPE_START( CDmeSoundClip, pTrackGroup, pTrack, pSoundClip )
const char *pGameSoundName = pSoundClip->m_Sound->m_GameSoundName;
if ( !pGameSoundName || !pGameSoundName[0] )
continue;
if ( pSoundClip->IsMute() )
continue;
if ( !pSoundClip->BuildClipStack( &soundToGlobal, info.m_pMovie, pClip ) )
continue;
DmeTime_t tStart = CDmeClip::FromChildMediaTime( soundToGlobal, DMETIME_ZERO, false );
DmeTime_t tEnd = CDmeClip::FromChildMediaTime( soundToGlobal, pSoundClip->GetDuration(), false );
tStart -= tOffset;
tEnd -= tOffset;
if ( tStart >= info.m_tExportEnd || tEnd <= info.m_tExportStart )
continue;
const char *pName = pSoundClip->GetName();
CDmElement *pSoundEvent = CreateElement<CDmElement>( pName );
pSoundEvent->SetValue( "start", tStart.GetTenthsOfMS() );
pSoundEvent->SetValue( "end", tEnd.GetTenthsOfMS() );
pSoundEvent->SetValue( "gamesound", pGameSoundName );
sounds.AddToTail( pSoundEvent );
DMETRACKGROUP_FOREACH_CLIP_TYPE_END()
}
}
static void ExportSounds_R( ExportInfo_t &info, CDmElement *pRoot, CDmeClip *pClip, DmeTime_t tOffset )
{
ExportSounds( info, pRoot, pClip, tOffset );
// Recurse
DmeClipStack_t childToGlobal;
int gc = pClip->GetTrackGroupCount();
for ( int i = 0; i < gc; ++i )
{
CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
DMETRACKGROUP_FOREACH_CLIP_START( pTrackGroup, pTrack, pChild )
if ( !pChild->BuildClipStack( &childToGlobal, info.m_pMovie, pClip ) )
continue;
DmeTime_t tStart = CDmeClip::FromChildMediaTime( childToGlobal, DMETIME_ZERO, false );
DmeTime_t tEnd = CDmeClip::FromChildMediaTime( childToGlobal, pChild->GetDuration(), false );
tStart -= tOffset;
tEnd -= tOffset;
if ( tStart >= info.m_tExportEnd || tEnd <= info.m_tExportStart )
continue;
ExportSounds_R( info, pRoot, pChild, tOffset );
DMETRACKGROUP_FOREACH_CLIP_END()
}
}
//-----------------------------------------------------------------------------
// Exports sounds, default implementation
//-----------------------------------------------------------------------------
static void ExportSounds( ExportInfo_t &info, CDmElement *pRoot )
{
DmeTime_t tOffset = info.m_pShot->GetStartTime();
ExportSounds( info, pRoot, info.m_pMovie, tOffset );
ExportSounds_R( info, pRoot, info.m_pShot, tOffset );
}
//-----------------------------------------------------------------------------
// Exports an .fac file
//-----------------------------------------------------------------------------
bool ExportFacialAnimation( const char *pFileName, CDmeFilmClip *pMovie, CDmeFilmClip *pShot, CDmeAnimationSet *pAnimationSet )
{
if ( !pMovie || !pShot || !pAnimationSet )
return false;
const char *pFileFormat = "facial_animation";
CDmElement *pRoot = CreateElement< CDmElement >( pAnimationSet->GetName() );
ExportInfo_t info;
info.m_pMovie = pMovie;
info.m_pShot = pShot;
info.m_pAnimationSet = pAnimationSet;
GetExportTimeRange( &info.m_tExportStart, &info.m_tExportEnd, pShot );
CDmeGameModel *pGameModel = pAnimationSet->GetValueElement<CDmeGameModel>( "gameModel" );
if ( pGameModel )
{
pRoot->SetValue( "gamemodel", pGameModel->GetModelName() );
}
ExportAnimations( info, pRoot );
ExportSounds( info, pRoot );
pRoot->SetFileId( DMFILEID_INVALID, TD_DEEP );
const char *pEncoding = "keyvalues2_flat";
bool bOk = g_pDataModel->SaveToFile( pFileName, NULL, pEncoding, pFileFormat, pRoot );
DestroyElement( pRoot, TD_DEEP );
return bOk;
}

View File

@ -0,0 +1,951 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// A class used to build flex animation controls for an animation set
//
//=============================================================================
#include "sfmobjects/flexcontrolbuilder.h"
#include "studio.h"
#include "movieobjects/dmeanimationset.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmetrackgroup.h"
#include "movieobjects/dmetrack.h"
#include "movieobjects/dmegamemodel.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmebalancetostereocalculatoroperator.h"
#include "tier1/utlsymbol.h"
// Names of attributes in controls we attach channels to
#define CONTROL_CHANNEL_ATTRIBUTE_COUNT 4
static const char *s_pChannelControls[CONTROL_CHANNEL_ATTRIBUTE_COUNT] =
{
"channel", "valuechannel", "balancechannel", "multilevelchannel"
};
//-----------------------------------------------------------------------------
// Flex controller
//-----------------------------------------------------------------------------
class CDefaultGlobalFlexController : public IGlobalFlexController
{
public:
CDefaultGlobalFlexController() : m_SymbolTable( 0, 32, true ) {}
virtual int FindGlobalFlexController( const char *name )
{
return m_SymbolTable.AddString( name );
}
virtual const char *GetGlobalFlexControllerName( int idx )
{
return m_SymbolTable.String( (CUtlSymbol)idx );
}
private:
CUtlSymbolTable m_SymbolTable;
};
static CDefaultGlobalFlexController s_GlobalFlexController;
extern IGlobalFlexController *g_pGlobalFlexController;
//-----------------------------------------------------------------------------
// This builds a list of the desired flex controllers we need to have controls for
// by the time we're all done with this enormous process.
//-----------------------------------------------------------------------------
void CFlexControlBuilder::BuildDesiredFlexControlList( CDmeGameModel *pGameModel )
{
CStudioHdr cHdr( pGameModel->GetStudioHdr() );
LocalFlexController_t nCount = cHdr.numflexcontrollers();
m_FlexControllerInfo.EnsureCapacity( nCount );
for ( LocalFlexController_t i = LocalFlexController_t(0); i < nCount; ++i )
{
int j = m_FlexControllerInfo.AddToTail();
FlexControllerInfo_t& info = m_FlexControllerInfo[j];
mstudioflexcontroller_t *pFlex = cHdr.pFlexcontroller( i );
Q_strncpy( info.m_pFlexControlName, pFlex->pszName(), sizeof( info.m_pFlexControlName ) );
info.m_nGlobalIndex = g_pGlobalFlexController->FindGlobalFlexController( pFlex->pszName() );
info.m_flDefaultValue = 0.0f;
if ( pFlex->max != pFlex->min )
{
// FIXME: Is this the correct default value?
info.m_flDefaultValue = ( 0.0f - pFlex->min ) / ( pFlex->max - pFlex->min );
}
}
}
//-----------------------------------------------------------------------------
// This builds a list of the desired input controls we need to have controls for
// by the time we're all done with this enormous process.
//-----------------------------------------------------------------------------
void CFlexControlBuilder::BuildDesiredControlList( CDmeGameModel *pGameModel )
{
int nCount = m_FlexControllerInfo.Count();
for ( int i = 0; i < nCount; ++i )
{
int j = m_ControlInfo.AddToTail();
ControlInfo_t &controlInfo = m_ControlInfo[j];
memset( &controlInfo, 0, sizeof(ControlInfo_t) );
FlexControllerInfo_t& info = m_FlexControllerInfo[i];
const char *pFlexName = info.m_pFlexControlName;
// Deal with stereo/mono controls
if ( !Q_strnicmp( "right_", pFlexName, 6 ) && ( i < nCount - 1 ) )
{
FlexControllerInfo_t& leftInfo = m_FlexControllerInfo[i+1];
Assert( !Q_strnicmp( "left_", leftInfo.m_pFlexControlName, 5 ) );
controlInfo.m_bIsStereo = true;
controlInfo.m_pControllerIndex[ OUTPUT_RIGHT ] = i;
controlInfo.m_pControllerIndex[ OUTPUT_LEFT ] = i+1;
Q_strncpy( controlInfo.m_pControlName, pFlexName + 6, sizeof(controlInfo.m_pControlName) );
// Convert default values into value/balance
LeftRightToValueBalance( &controlInfo.m_pDefaultValue[ CONTROL_VALUE ],
&controlInfo.m_pDefaultValue[ CONTROL_BALANCE ],
leftInfo.m_flDefaultValue, info.m_flDefaultValue );
// Skip the 'left_' flex control
++i;
}
else
{
controlInfo.m_bIsStereo = false;
controlInfo.m_pControllerIndex[ OUTPUT_MONO ] = i;
controlInfo.m_pControllerIndex[ OUTPUT_LEFT ] = -1;
Q_strncpy( controlInfo.m_pControlName, pFlexName, sizeof(controlInfo.m_pControlName) );
controlInfo.m_pDefaultValue[ CONTROL_VALUE ] = info.m_flDefaultValue;
controlInfo.m_pDefaultValue[ CONTROL_BALANCE ] = 0.5f;
}
// Deal with multi controls
controlInfo.m_bIsMulti = ( i+1 < nCount ) && !Q_strnicmp( "multi_", m_FlexControllerInfo[ i+1 ].m_pFlexControlName, 6 );
if ( controlInfo.m_bIsMulti )
{
FlexControllerInfo_t& multiInfo = m_FlexControllerInfo[i+1];
controlInfo.m_pControllerIndex[ OUTPUT_MULTILEVEL ] = i+1;
controlInfo.m_pDefaultValue[ CONTROL_MULTILEVEL ] = multiInfo.m_flDefaultValue;
// Skip the 'multi_' flex control
++i;
}
else
{
controlInfo.m_pControllerIndex[ OUTPUT_MULTILEVEL ] = -1;
controlInfo.m_pDefaultValue[ CONTROL_MULTILEVEL ] = 0.5f;
}
}
}
//-----------------------------------------------------------------------------
// Finds a desired flex controller index in the m_FlexControllerInfo array
//-----------------------------------------------------------------------------
int CFlexControlBuilder::FindDesiredFlexController( const char *pFlexControllerName ) const
{
int nCount = m_FlexControllerInfo.Count();
for ( int i = 0; i < nCount; ++i )
{
if ( !Q_stricmp( pFlexControllerName, m_FlexControllerInfo[i].m_pFlexControlName ) )
return i;
}
return -1;
}
//-----------------------------------------------------------------------------
// Removes a channel from the channels clip referring to it.
//-----------------------------------------------------------------------------
void CFlexControlBuilder::RemoveChannelFromClips( CDmeChannel *pChannel )
{
// First, try to grab the channels referring to this op
CUtlVector< CDmeChannelsClip* > channelsClips;
FindAncestorsReferencingElement( pChannel, channelsClips );
int nChannelsClips = channelsClips.Count();
for ( int i = 0; i < nChannelsClips; ++i )
{
channelsClips[ i ]->RemoveChannel( pChannel );
}
// Next, remove the channel from values controls it may be attached to
for ( int i = 0; i < CONTROL_CHANNEL_ATTRIBUTE_COUNT; ++i )
{
UtlSymId_t symChannelControl = g_pDataModel->GetSymbol( s_pChannelControls[i] );
CDmElement *pControl = FindReferringElement< CDmElement >( pChannel, symChannelControl );
if ( pControl )
{
pControl->RemoveAttribute( s_pChannelControls[i] );
}
}
}
//-----------------------------------------------------------------------------
// Removes a stereo operator from the animation set referring to it
//-----------------------------------------------------------------------------
void CFlexControlBuilder::RemoveStereoOpFromSet( CDmeBalanceToStereoCalculatorOperator *pSteroOp )
{
// First, try to grab the channel referring to this op
const static UtlSymId_t symOperators = g_pDataModel->GetSymbol( "operators" );
CDmeAnimationSet *pAnimationSet = FindReferringElement< CDmeAnimationSet >( pSteroOp, symOperators );
if ( pAnimationSet )
{
pAnimationSet->RemoveOperator( pSteroOp );
}
}
//-----------------------------------------------------------------------------
// Blows away the various elements trying to control a flex controller op
//-----------------------------------------------------------------------------
void CFlexControlBuilder::CleanupExistingFlexController( CDmeGameModel *pGameModel, CDmeGlobalFlexControllerOperator *pOp )
{
CDmeBalanceToStereoCalculatorOperator *pStereoOp;
// First, try to grab the channel referring to this op
const static UtlSymId_t symToElement = g_pDataModel->GetSymbol( "toElement" );
CDmeChannel *pChannel = FindReferringElement< CDmeChannel >( pOp, symToElement );
if ( !pChannel )
goto destroyOp;
// Sometimes a stereo op will be read from by this channel
pStereoOp = CastElement< CDmeBalanceToStereoCalculatorOperator >( pChannel->GetFromElement() );
RemoveChannelFromClips( pChannel );
DestroyElement( pChannel );
if ( !pStereoOp )
goto destroyOp;
RemoveStereoOpFromSet( pStereoOp );
// If we have a stereo op, then blow away all channels targetting that stereo op
DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pStereoOp->GetHandle() );
DmAttributeReferenceIterator_t next;
for ( ; i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; i = next )
{
next = g_pDataModel->NextAttributeReferencingElement( i );
CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
pChannel = CastElement<CDmeChannel>( pAttribute->GetOwner() );
if ( pChannel && pAttribute->GetNameSymbol() == symToElement )
{
RemoveChannelFromClips( pChannel );
DestroyElement( pChannel );
}
}
DestroyElement( pStereoOp );
destroyOp:
pGameModel->RemoveGlobalFlexController( pOp );
DestroyElement( pOp );
}
bool RemoveChannelIfUnused( CDmeChannel *pChannel, CDmeChannelsClip *pChannelsClip )
{
if ( !pChannel )
return false;
if ( pChannel->GetToElement() != NULL )
return false;
pChannelsClip->RemoveChannel( pChannel );
DestroyElement( pChannel );
return true;
}
// finds controls whose channels don't point to anything anymore, and deletes both the channels and the control
void CFlexControlBuilder::RemoveUnusedControlsAndChannels( CDmeAnimationSet *pAnimationSet, CDmeChannelsClip *pChannelsClip )
{
CDmrElementArray<> controls = pAnimationSet->GetControls();
int nControls = controls.Count();
for ( int i = nControls - 1; i >= 0 ; --i )
{
CDmElement *pControl = controls[ i ];
if ( pControl )
{
bool bRemoved = RemoveChannelIfUnused( pControl->GetValueElement< CDmeChannel >( "channel" ), pChannelsClip );
bRemoved = bRemoved || RemoveChannelIfUnused( pControl->GetValueElement< CDmeChannel >( "valuechannel" ), pChannelsClip );
bRemoved = bRemoved || RemoveChannelIfUnused( pControl->GetValueElement< CDmeChannel >( "balancechannel" ), pChannelsClip );
bRemoved = bRemoved || RemoveChannelIfUnused( pControl->GetValueElement< CDmeChannel >( "multilevelchannel" ), pChannelsClip );
if ( !bRemoved )
continue;
DestroyElement( pControl );
}
controls.Remove( i );
}
}
//-----------------------------------------------------------------------------
// This removes existing controls on the animationset that aren't in the desired state
//-----------------------------------------------------------------------------
void CFlexControlBuilder::RemoveUnusedExistingFlexControllers( CDmeGameModel *pGameModel )
{
// These are the current flex controllers
// NOTE: Name of these controllers should match the names of the flex controllers
int nCount = pGameModel->NumGlobalFlexControllers();
for ( int i = nCount; --i >= 0; )
{
CDmeGlobalFlexControllerOperator *pOp = pGameModel->GetGlobalFlexController( i );
Assert( pOp );
if ( pOp && FindDesiredFlexController( pOp->GetName() ) < 0 )
{
Msg( "removing flex controller %s\n", pOp->GetName() );
CleanupExistingFlexController( pGameModel, pOp );
}
}
}
//-----------------------------------------------------------------------------
// Returns an existing mono log
//-----------------------------------------------------------------------------
void CFlexControlBuilder::GetExistingMonoLog( ExistingLogInfo_t *pExistingLog,
CDmeFilmClip *pClip, CDmeGlobalFlexControllerOperator *pMonoOp )
{
pExistingLog->m_pLog = NULL;
const static UtlSymId_t symToElement = g_pDataModel->GetSymbol( "toElement" );
CDmeChannel *pMonoChannel = FindReferringElement< CDmeChannel >( pMonoOp, symToElement );
if ( !pMonoChannel )
return;
// First, try to grab the channel referring to this op
CDmeFloatLog *pLog = CastElement< CDmeFloatLog >( pMonoChannel->GetLog() );
if ( !pLog )
return;
if ( ComputeChannelTimeTransform( &pExistingLog->m_GlobalOffset, &pExistingLog->m_flGlobalScale, pClip, pMonoChannel ) )
{
pExistingLog->m_pLog = pLog;
}
}
//-----------------------------------------------------------------------------
// Finds a channels clip containing a particular channel
//-----------------------------------------------------------------------------
CDmeChannelsClip* CFlexControlBuilder::FindChannelsClipContainingChannel( CDmeFilmClip *pClip, CDmeChannel *pSearch )
{
int gc = pClip->GetTrackGroupCount();
for ( int i = 0; i < gc; ++i )
{
CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
DMETRACKGROUP_FOREACH_CLIP_TYPE_START( CDmeChannelsClip, pTrackGroup, pTrack, pChannelsClip )
int nChannels = pChannelsClip->m_Channels.Count();
for ( int j = 0; j < nChannels; ++j )
{
CDmeChannel *pChannel = pChannelsClip->m_Channels[ j ];
if ( pChannel == pSearch )
return pChannelsClip;
}
DMETRACKGROUP_FOREACH_CLIP_TYPE_END()
}
return NULL;
}
//-----------------------------------------------------------------------------
// Computes a global offset and scale to convert from log time to global time
//-----------------------------------------------------------------------------
void CFlexControlBuilder::ComputeChannelTimeTransform( DmeTime_t *pOffset, double *pScale, CDmeChannelsClip *pChannelsClip )
{
// Determine the global time of the start + end of the log
DmeClipStack_t srcStack;
pChannelsClip->BuildClipStack( &srcStack, m_pMovie, NULL );
*pOffset = CDmeClip::FromChildMediaTime( srcStack, DMETIME_ZERO, false );
DmeTime_t duration = CDmeClip::FromChildMediaTime( srcStack, DmeTime_t( 10000 ), false );
duration -= *pOffset;
*pScale = duration.GetSeconds();
}
bool CFlexControlBuilder::ComputeChannelTimeTransform( DmeTime_t *pOffset, double *pScale, CDmeFilmClip* pClip, CDmeChannel* pChannel )
{
CDmeChannelsClip *pChannelsClip = FindChannelsClipContainingChannel( pClip, pChannel );
if ( !pChannelsClip )
return false;
ComputeChannelTimeTransform( pOffset, pScale, pChannelsClip );
return true;
}
//-----------------------------------------------------------------------------
// Returns an existing value/balance log
//-----------------------------------------------------------------------------
void CFlexControlBuilder::GetExistingVBLog( ExistingLogInfo_t *pLogs, CDmeFilmClip *pClip, CDmeBalanceToStereoCalculatorOperator *pStereoOp )
{
// Stereo operators always have value/balance logs attached
DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pStereoOp->GetHandle() );
for ( ; i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; i = g_pDataModel->NextAttributeReferencingElement( i ) )
{
CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
CDmeChannel *pChannel = CastElement< CDmeChannel >( pAttribute->GetOwner() );
const static UtlSymId_t symToElement = g_pDataModel->GetSymbol( "toElement" );
if ( !pChannel || pAttribute->GetNameSymbol() != symToElement )
continue;
const char *pToAttributeName = pChannel->GetToAttribute()->GetName();
int nLogIndex = -1;
if ( !Q_stricmp( pToAttributeName, "value" ) )
{
nLogIndex = CONTROL_VALUE;
}
else if ( !Q_stricmp( pToAttributeName, "balance" ) )
{
nLogIndex = CONTROL_BALANCE;
}
else
{
continue;
}
CDmeFloatLog *pLog = CastElement< CDmeFloatLog >( pChannel->GetLog() );
if ( !pLog )
continue;
// Compute a scale and offset transforming log time into global time
if ( !ComputeChannelTimeTransform( &pLogs[nLogIndex].m_GlobalOffset, &pLogs[nLogIndex].m_flGlobalScale, pClip, pChannel ) )
continue;
// Detach the
pLogs[nLogIndex].m_pLog = pLog;
pChannel->SetLog( NULL ); // Detach
}
}
static void AddKeyToLogs( CDmeTypedLog< float > *valueLog, CDmeTypedLog< float > *balanceLog, const DmeTime_t& keyTime, float lval, float rval )
{
// Convert left right into value, balance
float value, balance;
LeftRightToValueBalance( &value, &balance, lval, rval );
// Msg( "%.5f setting l/r %f %f to value %f balance %f\n",
// keyTime.GetSeconds(), lval, rval, value, balance );
valueLog->SetKey( keyTime, value );
balanceLog->SetKey( keyTime, balance );
}
static void ConvertLRToVBLog( CDmeFloatLog *pValueLog, CDmeFloatLog *pBalanceLog, CDmeFloatLog *pLeftLog, CDmeFloatLog *pRightLog, DmeTime_t rightOffset, double flRightScale )
{
int lc = pLeftLog->GetKeyCount();
int rc = pRightLog->GetKeyCount();
int nLeft = 0, nRight = 0;
while ( nLeft < lc || nRight < rc )
{
bool bUseLeft = ( nLeft < lc );
bool bUseRight = ( nRight < rc );
DmeTime_t leftKeyTime = bUseLeft ? pLeftLog->GetKeyTime( nLeft ) : DMETIME_MAXTIME;
DmeTime_t rightKeyTime = bUseRight ? pRightLog->GetKeyTime( nRight ) : DMETIME_MAXTIME;
// Transform rightKeyTime into leftKeyTime space
if ( bUseRight )
{
rightKeyTime.SetSeconds( rightKeyTime.GetSeconds() * flRightScale );
rightKeyTime += rightOffset;
}
if ( leftKeyTime == rightKeyTime )
{
float lval = pLeftLog->GetKeyValue( nLeft++ );
float rval = pRightLog->GetKeyValue( nRight++ );
AddKeyToLogs( pValueLog, pBalanceLog, leftKeyTime, lval, rval );
continue;
}
if ( leftKeyTime < rightKeyTime )
{
// pull a value from the right log at the leftKeyTime
// and advance to the next sample on the left side
float lval = pLeftLog->GetKeyValue( nLeft++ );
float rval = pRightLog->GetValue( leftKeyTime );
AddKeyToLogs( pValueLog, pBalanceLog, leftKeyTime, lval, rval );
continue;
}
// Pull a value from the left log at the rightKeyTime
// and advance to the next sample on the right side
float lval = pLeftLog->GetValue( rightKeyTime );
float rval = pRightLog->GetKeyValue( nRight++ );
AddKeyToLogs( pValueLog, pBalanceLog, rightKeyTime, lval, rval );
}
}
//-----------------------------------------------------------------------------
// Converts an existing value/balance log
//-----------------------------------------------------------------------------
void CFlexControlBuilder::ConvertExistingLRLogs( ExistingLogInfo_t *pLogs,
CDmeFilmClip *pClip, CDmeChannel *pLeftChannel, CDmeChannel *pRightChannel )
{
CDmeFloatLog *pRightLog = CastElement< CDmeFloatLog >( pRightChannel->GetLog() );
CDmeFloatLog *pLeftLog = CastElement< CDmeFloatLog >( pLeftChannel->GetLog() );
if ( !pRightLog || !pLeftLog )
return;
// Compute a scale + offset to transform the right log to get it in the same space as the left log
DmeTime_t leftOffset, rightOffset;
double flLeftScale, flRightScale;
if ( !ComputeChannelTimeTransform( &leftOffset, &flLeftScale, pClip, pLeftChannel ) )
return;
if ( !ComputeChannelTimeTransform( &rightOffset, &flRightScale, pClip, pRightChannel ) )
return;
flRightScale = ( flRightScale != 0.0f ) ? flLeftScale / flRightScale : 1.0;
rightOffset = leftOffset - DmeTime_t( rightOffset.GetSeconds() * flRightScale );
pLogs[CONTROL_VALUE].m_pLog = CreateElement< CDmeFloatLog >( "value" );
pLogs[CONTROL_VALUE].m_GlobalOffset = leftOffset;
pLogs[CONTROL_VALUE].m_flGlobalScale = flLeftScale;
pLogs[CONTROL_BALANCE].m_pLog = CreateElement< CDmeFloatLog >( "balance" );
pLogs[CONTROL_BALANCE].m_GlobalOffset = leftOffset; // NOTE: This is correct! All logs are transformed into left channel time
pLogs[CONTROL_BALANCE].m_flGlobalScale = flLeftScale;
ConvertLRToVBLog( pLogs[CONTROL_VALUE].m_pLog, pLogs[CONTROL_BALANCE].m_pLog,
pLeftLog, pRightLog, rightOffset, flRightScale );
// DestroyElement( pLeftLog );
// DestroyElement( pRightLog );
}
//-----------------------------------------------------------------------------
// Returns an existing stereo log, performing conversion if necessary
//-----------------------------------------------------------------------------
void CFlexControlBuilder::GetExistingStereoLog( ExistingLogInfo_t *pLogs, CDmeFilmClip *pClip,
CDmeGlobalFlexControllerOperator *pRightOp, CDmeGlobalFlexControllerOperator *pLeftOp )
{
pLogs[CONTROL_VALUE].m_pLog = NULL;
pLogs[CONTROL_BALANCE].m_pLog = NULL;
// First, try to grab the channel referring to this op
const static UtlSymId_t symToElement = g_pDataModel->GetSymbol( "toElement" );
CDmeChannel *pChannel = FindReferringElement< CDmeChannel >( pRightOp, symToElement );
if ( !pChannel )
return;
// Sometimes a stereo op will be read from by this channel
CDmeBalanceToStereoCalculatorOperator *pStereoOp = CastElement< CDmeBalanceToStereoCalculatorOperator >( pChannel->GetFromElement() );
if ( pStereoOp )
{
GetExistingVBLog( pLogs, pClip, pStereoOp );
return;
}
// In this case, we recorded game data and we have left/right logs
CDmeChannel *pLeftChannel = FindReferringElement< CDmeChannel >( pLeftOp, symToElement );
if ( !pLeftChannel )
return;
ConvertExistingLRLogs( pLogs, pClip, pLeftChannel, pChannel );
}
//-----------------------------------------------------------------------------
// Fixup list of existing flex controller logs
// - reattach flex controls that were removed from the gamemodel's list
//-----------------------------------------------------------------------------
void CFlexControlBuilder::FixupExistingFlexControlLogList( CDmeFilmClip *pCurrentClip, CDmeGameModel *pGameModel )
{
int nTrackGroups = pCurrentClip->GetTrackGroupCount();
for ( int gi = 0; gi < nTrackGroups; ++gi )
{
CDmeTrackGroup *pTrackGroup = pCurrentClip->GetTrackGroup( gi );
if ( !pTrackGroup )
continue;
DMETRACKGROUP_FOREACH_CLIP_TYPE_START( CDmeChannelsClip, pTrackGroup, pTrack, pChannelsClip )
int nChannels = pChannelsClip->m_Channels.Count();
for ( int ci = 0; ci < nChannels; ++ci )
{
CDmeChannel *pChannel = pChannelsClip->m_Channels[ ci ];
if ( !pChannel )
continue;
CDmeGlobalFlexControllerOperator *pOp = CastElement< CDmeGlobalFlexControllerOperator >( pChannel->GetToElement() );
if ( !pOp )
continue;
if ( pOp->m_gameModel != pGameModel->GetHandle() )
continue;
int nGlobalIndex = pOp->GetGlobalIndex();
CDmeGlobalFlexControllerOperator *pFoundOp = pGameModel->FindGlobalFlexController( nGlobalIndex );
if ( pFoundOp == pOp )
continue;
if ( !pFoundOp )
{
Msg( "adding missing flex controller %d %s\n", nGlobalIndex, pOp->GetName() );
pFoundOp = pGameModel->AddGlobalFlexController( pOp->GetName(), nGlobalIndex );
}
pChannel->SetOutput( pFoundOp, pChannel->GetToAttribute()->GetName() );
if ( pChannel->GetFromElement() == pOp )
{
pChannel->SetInput( pFoundOp, pChannel->GetFromAttribute()->GetName() );
}
Msg( "removing duplicate flex controller %d %s\n", nGlobalIndex, pOp->GetName() );
RemoveElementFromRefereringAttributes( pOp );
DestroyElement( pOp );
}
DMETRACKGROUP_FOREACH_CLIP_TYPE_END();
}
}
//-----------------------------------------------------------------------------
// Build list of existing flex controller logs
//-----------------------------------------------------------------------------
void CFlexControlBuilder::BuildExistingFlexControlLogList( CDmeFilmClip *pCurrentClip, CDmeGameModel *pGameModel )
{
// These are the current flex controllers that also exist in the desired list
// NOTE: Name of these controllers should match the names of the flex controllers
int nCount = m_ControlInfo.Count();
for ( int i = 0; i < nCount; ++i )
{
ControlInfo_t &info = m_ControlInfo[i];
if ( info.m_bIsStereo )
{
int nRightFlex = info.m_pControllerIndex[ OUTPUT_RIGHT ];
int nLeftFlex = info.m_pControllerIndex[ OUTPUT_LEFT ];
FlexControllerInfo_t *pRightInfo = &m_FlexControllerInfo[nRightFlex];
FlexControllerInfo_t *pLeftInfo = &m_FlexControllerInfo[nLeftFlex];
CDmeGlobalFlexControllerOperator *pRightOp = pGameModel->FindGlobalFlexController( pRightInfo->m_nGlobalIndex );
CDmeGlobalFlexControllerOperator *pLeftOp = pGameModel->FindGlobalFlexController( pLeftInfo->m_nGlobalIndex );
if ( pRightOp && pLeftOp )
{
Msg( "replacing stereo flex controllers %s and %s\n", pRightOp->GetName(), pRightOp->GetName() );
GetExistingStereoLog( info.m_pExistingLog, pCurrentClip, pRightOp, pLeftOp );
CleanupExistingFlexController( pGameModel, pRightOp );
CleanupExistingFlexController( pGameModel, pLeftOp );
}
}
else
{
int nFlex = info.m_pControllerIndex[ OUTPUT_MONO ];
FlexControllerInfo_t *pInfo = &m_FlexControllerInfo[nFlex];
CDmeGlobalFlexControllerOperator *pMonoOp = pGameModel->FindGlobalFlexController( pInfo->m_nGlobalIndex );
if ( pMonoOp )
{
Msg( "replacing mono flex controller %s\n", pMonoOp->GetName() );
GetExistingMonoLog( &info.m_pExistingLog[CONTROL_VALUE], pCurrentClip, pMonoOp );
CleanupExistingFlexController( pGameModel, pMonoOp );
}
}
if ( info.m_bIsMulti )
{
int nFlex = info.m_pControllerIndex[ OUTPUT_MULTILEVEL ];
FlexControllerInfo_t *pMultiInfo = &m_FlexControllerInfo[ nFlex ];
CDmeGlobalFlexControllerOperator *pMultiOp = pGameModel->FindGlobalFlexController( pMultiInfo->m_nGlobalIndex );
if ( pMultiOp )
{
Msg( "replacing multi flex controller %s\n", pMultiOp->GetName() );
GetExistingMonoLog( &info.m_pExistingLog[CONTROL_MULTILEVEL], pCurrentClip, pMultiOp );
CleanupExistingFlexController( pGameModel, pMultiOp );
}
}
}
}
//-----------------------------------------------------------------------------
// Creates a flex controller and a channel connecting it to a control
//-----------------------------------------------------------------------------
struct FlexOpInfo_t
{
const char *m_pControlAttributeName;
const char *m_pControlLinkAttributeName;
};
static FlexOpInfo_t s_pFlexOpInfo[2] =
{
{ "value", "" },
{ "multilevel", "multilevel" },
};
void CFlexControlBuilder::BuildFlexControllerOps( CDmeGameModel *pGameModel, CDmeChannelsClip *pChannelsClip, ControlInfo_t &info, ControlField_t field )
{
const FlexOpInfo_t& flexInfo = s_pFlexOpInfo[ ( field == CONTROL_VALUE ) ? 0 : 1 ];
// Get the global flex controller name and index
const FlexControllerInfo_t& fcInfo = m_FlexControllerInfo[ info.m_pControllerIndex[field] ];
// Create operator which drives facial flex setting
CDmeGlobalFlexControllerOperator *pFlexControllerOp = pGameModel->AddGlobalFlexController(
fcInfo.m_pFlexControlName, fcInfo.m_nGlobalIndex );
// Create a channel which passes from the control value to the global flex controller
char pName[ 256 ];
Q_snprintf( pName, sizeof( pName ), "%s_flex_channel", fcInfo.m_pFlexControlName );
info.m_ppControlChannel[field] = pChannelsClip->CreatePassThruConnection( pName,
info.m_pControl, flexInfo.m_pControlAttributeName, pFlexControllerOp, "flexWeight" );
// NOTE: The animation set slider panel looks for these custom attributes
Q_snprintf( pName, sizeof(pName), "%schannel", flexInfo.m_pControlLinkAttributeName );
info.m_pControl->SetValue( pName, info.m_ppControlChannel[field] );
// Switch the channel into play mode by default
info.m_ppControlChannel[field]->SetMode( CM_PLAY );
}
//-----------------------------------------------------------------------------
// Creates a flex controller and a channel connecting it to stereo controls
//-----------------------------------------------------------------------------
static const char *s_pStereoOutputPrefix[2] =
{
"right",
"left",
};
static const char *s_pStereoInputPrefix[2] =
{
"value",
"balance",
};
void CFlexControlBuilder::BuildStereoFlexControllerOps( CDmeAnimationSet *pAnimationSet,
CDmeGameModel *pGameModel, CDmeChannelsClip *pChannelsClip, ControlInfo_t &info )
{
// Create an operator which converts value/balance to left/right
CDmrElementArray< CDmeOperator > operators = pAnimationSet->GetOperators();
CDmeBalanceToStereoCalculatorOperator *pStereoCalcOp =
CreateElement< CDmeBalanceToStereoCalculatorOperator >( info.m_pControlName, pAnimationSet->GetFileId() );
operators.AddToTail( pStereoCalcOp->GetHandle() );
pStereoCalcOp->SetValue< float >( "value", info.m_pDefaultValue[CONTROL_VALUE] );
pStereoCalcOp->SetValue< float >( "balance", info.m_pDefaultValue[CONTROL_BALANCE] );
// Connect channels from animation set controls to balance operator to flex controller operators
char pChannelName[ 256 ];
char pResultName[ 256 ];
for ( int i = 0; i < 2; ++i )
{
// Get the global flex controller name and index
const FlexControllerInfo_t& fcInfo = m_FlexControllerInfo[ info.m_pControllerIndex[i] ];
// Create an operator which drives facial flex setting
CDmeGlobalFlexControllerOperator *pFlexControllerOp = pGameModel->AddGlobalFlexController(
fcInfo.m_pFlexControlName, fcInfo.m_nGlobalIndex );
// Now create a channel which connects the output of the stereo op to the flex controller op
Q_snprintf( pResultName, sizeof( pResultName ), "result_%s", s_pStereoOutputPrefix[ i ] );
Q_snprintf( pChannelName, sizeof( pChannelName ), "%s_flex_channel", fcInfo.m_pFlexControlName );
pChannelsClip->CreatePassThruConnection( pChannelName, pStereoCalcOp,
pResultName, pFlexControllerOp, "flexWeight" );
// Create a channel which connects the control to the input of the stereo op
Q_snprintf( pChannelName, sizeof( pChannelName ), "%s_%s_channel", info.m_pControlName, s_pStereoInputPrefix[ i ] );
info.m_ppControlChannel[i] = pChannelsClip->CreatePassThruConnection( pChannelName,
info.m_pControl, s_pStereoInputPrefix[ i ], pStereoCalcOp, s_pStereoInputPrefix[ i ] );
// NOTE: The animation set slider panel looks for these custom attributes
Q_snprintf( pChannelName, sizeof(pChannelName), "%schannel", s_pStereoInputPrefix[ i ] );
info.m_pControl->SetValue( pChannelName, info.m_ppControlChannel[i] );
// Switch the channel into play mode by default
info.m_ppControlChannel[i]->SetMode( CM_PLAY );
}
}
//-----------------------------------------------------------------------------
// Build the infrastructure of the ops that connect that control to the dmegamemodel
//-----------------------------------------------------------------------------
void CFlexControlBuilder::AttachControlsToGameModel( CDmeAnimationSet *pAnimationSet,
CDmeGameModel *pGameModel, CDmeChannelsClip *pChannelsClip )
{
// Build the infrastructure of the ops that connect that control to the dmegamemodel
int c = m_ControlInfo.Count();
for ( int i = 0; i < c; ++i )
{
ControlInfo_t &info = m_ControlInfo[i];
if ( info.m_bIsStereo )
{
BuildStereoFlexControllerOps( pAnimationSet, pGameModel, pChannelsClip, info );
}
else
{
BuildFlexControllerOps( pGameModel, pChannelsClip, info, CONTROL_VALUE );
}
if ( info.m_bIsMulti )
{
BuildFlexControllerOps( pGameModel, pChannelsClip, info, CONTROL_MULTILEVEL );
}
}
}
//-----------------------------------------------------------------------------
// Initializes the fields of a flex control
//-----------------------------------------------------------------------------
void CFlexControlBuilder::InitializeFlexControl( ControlInfo_t &info )
{
CDmElement *pControl = info.m_pControl;
// Remove these, if they exist...
for ( int i = 0; i < CONTROL_CHANNEL_ATTRIBUTE_COUNT; ++i )
{
pControl->RemoveAttribute( s_pChannelControls[i] );
}
// Force these to always be up-to-date
pControl->SetValue< bool >( "combo", info.m_bIsStereo );
pControl->SetValue< bool >( "multi", info.m_bIsMulti );
pControl->SetValue< float >( "defaultValue", info.m_pDefaultValue[CONTROL_VALUE] );
pControl->SetValue< float >( "defaultBalance", info.m_pDefaultValue[CONTROL_BALANCE] );
pControl->SetValue< float >( "defaultMultilevel", info.m_pDefaultValue[CONTROL_MULTILEVEL] );
// These can keep their value if they already exist
pControl->InitValue< float >( "value", info.m_pDefaultValue[CONTROL_VALUE] );
if ( info.m_bIsStereo )
{
pControl->InitValue< float >( "balance", info.m_pDefaultValue[CONTROL_BALANCE] );
}
else
{
pControl->RemoveAttribute( "balance" );
}
if ( info.m_bIsMulti )
{
pControl->InitValue< float >( "multilevel", info.m_pDefaultValue[CONTROL_MULTILEVEL] );
}
else
{
pControl->RemoveAttribute( "multilevel" );
}
}
//-----------------------------------------------------------------------------
// Creates all controls for flexes
//-----------------------------------------------------------------------------
void CFlexControlBuilder::CreateFlexControls( CDmeAnimationSet *pAnimationSet )
{
// Create a facial control for all input controls
int c = m_ControlInfo.Count();
for ( int i = 0; i < c; ++i )
{
ControlInfo_t &info = m_ControlInfo[i];
// Check to see if the animation set already has the control
info.m_pControl = pAnimationSet->FindOrAddControl( info.m_pControlName );
// Now initialize the fields of the flex control
InitializeFlexControl( info );
}
}
//-----------------------------------------------------------------------------
// Attaches existing logs and sets default values for logs
//-----------------------------------------------------------------------------
void CFlexControlBuilder::SetupLogs( CDmeChannelsClip *pChannelsClip, bool bUseExistingLogs )
{
DmeTime_t targetOffset;
double flTargetScale;
ComputeChannelTimeTransform( &targetOffset, &flTargetScale, pChannelsClip );
double flOOTargetScale = ( flTargetScale != 0.0 ) ? 1.0 / flTargetScale : 1.0;
// Build the infrastructure of the ops that connect that control to the dmegamemodel
int c = m_ControlInfo.Count();
for ( int i = 0; i < c; ++i )
{
ControlInfo_t &info = m_ControlInfo[i];
for ( int j = 0; j < CONTROL_FIELD_COUNT; ++j )
{
// Can happen for non-multi or non-stereo controls
if ( !info.m_ppControlChannel[j] )
continue;
// Replace the existing log if we need to
CDmeFloatLog *pFloatLog = CastElement< CDmeFloatLog >( info.m_ppControlChannel[j]->GetLog() );
if ( bUseExistingLogs && info.m_pExistingLog[j].m_pLog )
{
info.m_ppControlChannel[j]->SetLog( info.m_pExistingLog[j].m_pLog );
DestroyElement( pFloatLog );
pFloatLog = info.m_pExistingLog[j].m_pLog;
// Apply transform to get the log into the space of the current channel
double flTotalScale = info.m_pExistingLog[j].m_flGlobalScale * flOOTargetScale;
DmeTime_t totalOffset = info.m_pExistingLog[j].m_GlobalOffset - targetOffset;
totalOffset.SetSeconds( totalOffset.GetSeconds() * flOOTargetScale );
pFloatLog->ScaleBiasKeyTimes( flTotalScale, totalOffset );
}
// Set the default value for this log
pFloatLog->SetDefaultValue( info.m_pDefaultValue[j] );
}
}
}
//-----------------------------------------------------------------------------
// Main entry point for creating flex animation set controls
//-----------------------------------------------------------------------------
void CFlexControlBuilder::CreateAnimationSetControls( CDmeFilmClip *pMovie, CDmeAnimationSet *pAnimationSet,
CDmeGameModel *pGameModel, CDmeFilmClip *pSourceClip, CDmeChannelsClip *pDestClip, bool bUseExistingLogs )
{
m_pMovie = pMovie;
FixupExistingFlexControlLogList( pSourceClip, pGameModel );
// First, look at the current mdl and determine what are its low-level flexcontrollers
// [these are the outputs eventually driven by the animation set controls]
BuildDesiredFlexControlList( pGameModel );
// Next, based on the list of low-level flexcontrollers, determine a high-level set of input controls
BuildDesiredControlList( pGameModel );
// Next look at what the animation set currently thinks are the input controls + low-level flexcontrollers
// and remove the unused ones
RemoveUnusedExistingFlexControllers( pGameModel );
RemoveUnusedControlsAndChannels( pAnimationSet, pDestClip );
if ( bUseExistingLogs )
{
// Look at the current input controls + low-level flexcontrollers
// and grab logs that drive them so we can apply them to the new controls
BuildExistingFlexControlLogList( pSourceClip, pGameModel );
}
// Create the input controls we decided we needed in BuildDesiredControlList
CreateFlexControls( pAnimationSet );
// Build channels + control logis attaching the input controls to the low level flex controls
AttachControlsToGameModel( pAnimationSet, pGameModel, pDestClip );
// Attach existing logs to the new input controls created in CreateFlexControls
SetupLogs( pDestClip, bUseExistingLogs );
}
//-----------------------------------------------------------------------------
// Initialize default global flex controller
//-----------------------------------------------------------------------------
void SetupDefaultFlexController()
{
g_pGlobalFlexController = &s_GlobalFlexController;
}

View File

@ -0,0 +1,891 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// NOTE: This is a cut-and-paste hack job to get animation set construction
// working from a commandline tool. It came from tools/ifm/createsfmanimation.cpp
// This file needs to die almost immediately + be replaced with a better solution
// that can be used both by the sfm + sfmgen.
//
//=============================================================================
#include "sfmobjects/sfmanimationsetutils.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmetrackgroup.h"
#include "movieobjects/dmetrack.h"
#include "movieobjects/dmecamera.h"
#include "movieobjects/dmetimeselection.h"
#include "movieobjects/dmeanimationset.h"
#include "movieobjects/dmegamemodel.h"
#include "sfmobjects/flexcontrolbuilder.h"
#include "tier3/tier3.h"
#include "bone_setup.h"
#include "vstdlib/random.h"
#include "tier1/KeyValues.h"
#include "filesystem.h"
#include "movieobjects/timeutils.h"
#define ANIMATION_SET_DEFAULT_GROUP_MAPPING_FILE "cfg/SFM_DefaultAnimationGroups.txt"
#define STANDARD_CHANNEL_TRACK_GROUP "channelTrackGroup"
#define STANDARD_ANIMATIONSET_CHANNELS_TRACK "animSetEditorChannels"
#define CLIP_PREROLL_TIME DmeTime_t( 5.0f )
#define CLIP_POSTROLL_TIME DmeTime_t( 5.0f )
//-----------------------------------------------------------------------------
// Creates channels clip for the animation set
//-----------------------------------------------------------------------------
static CDmeChannelsClip* CreateChannelsClip( CDmeAnimationSet *pAnimationSet, CDmeFilmClip *pOwnerClip )
{
CDmeTrackGroup *pTrackGroup = pOwnerClip->FindOrAddTrackGroup( "channelTrackGroup" );
if ( !pTrackGroup )
{
Assert( 0 );
return NULL;
}
CDmeTrack *pAnimSetEditorTrack = pTrackGroup->FindOrAddTrack( "animSetEditorChannels", DMECLIP_CHANNEL );
Assert( pAnimSetEditorTrack );
CDmeChannelsClip *pChannelsClip = CreateElement< CDmeChannelsClip >( pAnimationSet->GetName(), pAnimationSet->GetFileId() );
pAnimSetEditorTrack->AddClip( pChannelsClip );
DmeTime_t childMediaTime = pOwnerClip->GetStartInChildMediaTime();
pChannelsClip->SetStartTime( childMediaTime - CLIP_PREROLL_TIME );
DmeTime_t childMediaDuration = pOwnerClip->ToChildMediaDuration( pOwnerClip->GetDuration() );
pChannelsClip->SetDuration( childMediaDuration + CLIP_PREROLL_TIME + CLIP_POSTROLL_TIME );
return pChannelsClip;
}
//-----------------------------------------------------------------------------
// Creates a constant valued log
//-----------------------------------------------------------------------------
template < class T >
CDmeChannel *CreateConstantValuedLog( CDmeChannelsClip *channelsClip, const char *basename, const char *pName, CDmElement *pToElement, const char *pToAttr, const T &value )
{
char name[ 256 ];
Q_snprintf( name, sizeof( name ), "%s_%s channel", basename, pName );
CDmeChannel *pChannel = CreateElement< CDmeChannel >( name, channelsClip->GetFileId() );
pChannel->SetMode( CM_PLAY );
pChannel->CreateLog( CDmAttributeInfo< T >::AttributeType() );
pChannel->SetOutput( pToElement, pToAttr );
pChannel->GetLog()->SetValueThreshold( 0.0f );
((CDmeTypedLog< T > *)pChannel->GetLog())->InsertKey( DmeTime_t( 0 ), value );
channelsClip->m_Channels.AddToTail( pChannel );
return pChannel;
}
//-----------------------------------------------------------------------------
// Create channels for transform data
//-----------------------------------------------------------------------------
static void CreateTransformChannels( CDmeTransform *pTransform, const char *pBaseName, int bi, CDmeChannelsClip *pChannelsClip )
{
char name[ 256 ];
// create, connect and cache bonePos channel
Q_snprintf( name, sizeof( name ), "%s_bonePos channel %d", pBaseName, bi );
CDmeChannel *pPosChannel = CreateElement< CDmeChannel >( name, pChannelsClip->GetFileId() );
pPosChannel->SetMode( CM_PLAY );
pPosChannel->CreateLog( AT_VECTOR3 );
pPosChannel->SetOutput( pTransform, "position" );
pPosChannel->GetLog()->SetValueThreshold( 0.0f );
pChannelsClip->m_Channels.AddToTail( pPosChannel );
// create, connect and cache boneRot channel
Q_snprintf( name, sizeof( name ), "%s_boneRot channel %d", pBaseName, bi );
CDmeChannel *pRotChannel = CreateElement< CDmeChannel >( name, pChannelsClip->GetFileId() );
pRotChannel->SetMode( CM_PLAY );
pRotChannel->CreateLog( AT_QUATERNION );
pRotChannel->SetOutput( pTransform, "orientation" );
pRotChannel->GetLog()->SetValueThreshold( 0.0f );
pChannelsClip->m_Channels.AddToTail( pRotChannel );
}
static void CreateAnimationLogs( CDmeChannelsClip *channelsClip, CDmeGameModel *pModel, studiohdr_t *pStudioHdr, const char *basename, int sequence, float flStartTime, float flDuration, float flTimeStep = 0.015f )
{
Assert( pModel );
Assert( pStudioHdr );
CStudioHdr hdr( pStudioHdr, g_pMDLCache );
if ( sequence >= hdr.GetNumSeq() )
{
sequence = 0;
}
int numbones = hdr.numbones();
// make room for bones
CUtlVector< CDmeDag* > dags;
CUtlVector< CDmeChannel * > poschannels;
CUtlVector< CDmeChannel * > rotchannels;
dags.EnsureCapacity( numbones );
poschannels.EnsureCapacity( numbones );
rotchannels.EnsureCapacity( numbones );
Vector pos[ MAXSTUDIOBONES ];
Quaternion q[ MAXSTUDIOBONES ];
float poseparameter[ MAXSTUDIOPOSEPARAM ];
for ( int pp = 0; pp < MAXSTUDIOPOSEPARAM; ++pp )
{
poseparameter[ pp ] = 0.0f;
}
float flSequenceDuration = Studio_Duration( &hdr, sequence, poseparameter );
mstudioseqdesc_t &seqdesc = hdr.pSeqdesc( sequence );
bool created = false;
for ( float t = flStartTime; t <= flStartTime + flDuration; t += flTimeStep )
{
int bi;
if ( t > flStartTime + flDuration )
t = flStartTime + flDuration;
float flCycle = t / flSequenceDuration;
if ( seqdesc.flags & STUDIO_LOOPING )
{
flCycle = flCycle - (int)flCycle;
if (flCycle < 0) flCycle += 1;
}
else
{
flCycle = max( 0.f, min( flCycle, 0.9999f ) );
}
if ( !created )
{
created = true;
// create, connect and cache each bone's pos and rot channels
for ( bi = 0; bi < numbones; ++bi )
{
int nCount = channelsClip->m_Channels.Count();
CDmeTransform *pTransform = pModel->GetBone( bi );
CreateTransformChannels( pTransform, basename, bi, channelsClip );
CDmeChannel *pPosChannel = channelsClip->m_Channels[ nCount ];
CDmeChannel *pRotChannel = channelsClip->m_Channels[ nCount+1 ];
poschannels.AddToTail( pPosChannel );
rotchannels.AddToTail( pRotChannel );
}
}
// Set up skeleton
IBoneSetup boneSetup( &hdr, BONE_USED_BY_ANYTHING, poseparameter );
boneSetup.InitPose( pos, q );
boneSetup.AccumulatePose( pos, q, sequence, flCycle, 1.0f, t, NULL );
// Copy bones into recording logs
for ( bi = 0 ; bi < numbones; ++bi )
{
((CDmeVector3Log *)poschannels[ bi ]->GetLog())->InsertKey( DmeTime_t( t ), pos[ bi ] );
((CDmeQuaternionLog *)rotchannels[ bi ]->GetLog())->InsertKey( DmeTime_t( t ), q[ bi ] );
}
}
}
static CDmeChannelsClip *FindChannelsClipTargetingDmeGameModel( CDmeFilmClip *pClip, CDmeGameModel *pGameModel )
{
uint nBoneCount = pGameModel->NumBones();
CDmeTransform *pGameModelTransform = pGameModel->GetTransform();
int gc = pClip->GetTrackGroupCount();
for ( int i = 0; i < gc; ++i )
{
CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
DMETRACKGROUP_FOREACH_CLIP_TYPE_START( CDmeChannelsClip, pTrackGroup, pTrack, pChannelsClip )
if ( FindChannelTargetingElement( pChannelsClip, pGameModel ) )
return pChannelsClip;
if ( FindChannelTargetingElement( pChannelsClip, pGameModelTransform ) )
return pChannelsClip;
for ( uint j = 0; j < nBoneCount; ++j )
{
if ( FindChannelTargetingElement( pChannelsClip, pGameModel->GetBone( j ) ) )
return pChannelsClip;
}
DMETRACKGROUP_FOREACH_CLIP_TYPE_END()
}
return NULL;
}
static void RetimeLogData( CDmeChannelsClip *pSrcChannelsClip, CDmeChannelsClip *pDstChannelsClip, CDmeLog *pLog )
{
float srcScale = pSrcChannelsClip->GetTimeScale();
float dstScale = pDstChannelsClip->GetTimeScale();
DmeTime_t srcStart = pSrcChannelsClip->GetStartTime();
DmeTime_t dstStart = pDstChannelsClip->GetStartTime();
DmeTime_t srcOffset = pSrcChannelsClip->GetTimeOffset();
DmeTime_t dstOffset = pDstChannelsClip->GetTimeOffset();
srcOffset -= srcStart;
dstOffset -= dstStart;
if ( srcScale != dstScale || srcOffset != dstOffset )
{
// for speed, I pulled out the math converting out of one timeframe into another:
// t = (t/f0-o0+s0 -s1+o1)*f1
// = t * f1/f0 + f1 * (o1-o0-s1+s0)
float scale = dstScale / srcScale;
DmeTime_t offset = dstScale * ( dstOffset - srcOffset );
int nKeys = pLog->GetKeyCount();
for ( int i = 0; i < nKeys; ++i )
{
DmeTime_t keyTime = pLog->GetKeyTime( i );
keyTime = keyTime * scale + offset;
pLog->SetKeyTime( i, keyTime );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Once bones have been setup and flex channels moved, the only things left should be:
// a channel logging the model's "visibility" state
// a channel logging the model's "sequence"
// a channel loggint the model's "viewtarget" position
//-----------------------------------------------------------------------------
static void TransferRemainingChannels( CDmeFilmClip *shot, CDmeChannelsClip *destClip, CDmeChannelsClip *srcClip )
{
if ( srcClip == destClip )
return;
int channelsCount = srcClip->m_Channels.Count();
for ( int i = 0; i < channelsCount; ++i )
{
// Remove channel from channels clip
CDmeChannel *channel = srcClip->m_Channels[ i ];
Assert( channel );
if ( !channel )
continue;
Msg( "Transferring '%s'\n", channel->GetName() );
destClip->m_Channels.AddToTail( channel );
channel->SetMode( CM_PLAY );
// Transfer the logs over to the
CDmeLog *log = channel->GetLog();
if ( log )
{
RetimeLogData( srcClip, destClip, log );
}
}
srcClip->m_Channels.RemoveAll();
// Now find the track which contains the srcClip and remove the srcClip from the track
for ( DmAttributeReferenceIterator_t it = g_pDataModel->FirstAttributeReferencingElement( srcClip->GetHandle() );
it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
it = g_pDataModel->NextAttributeReferencingElement( it ) )
{
CDmAttribute *attr = g_pDataModel->GetAttribute( it );
Assert( attr );
CDmElement *element = attr->GetOwner();
Assert( element );
if ( !element )
continue;
CDmeTrack *t = CastElement< CDmeTrack >( element );
if ( !t )
continue;
t->RemoveClip( srcClip );
g_pDataModel->DestroyElement( srcClip->GetHandle() );
break;
}
}
static void SetupBoneTransform( CDmeFilmClip *shot, CDmeChannelsClip *srcChannelsClip, CDmeChannelsClip *channelsClip,
CDmElement *control, CDmeGameModel *gameModel, const char *basename, studiohdr_t *hdr, int bonenum, const char *boneName, bool bAttachToGameRecording )
{
const char *channelNames[] = { "position", "orientation" };
const char *valueNames[] = { "valuePosition", "valueOrientation" };
const char *suffix[] = { "Pos", "Rot" };
DmAttributeType_t channelTypes[] = { AT_VECTOR3, AT_QUATERNION };
int i;
CDmeTransform *pBoneTxForm = gameModel->GetBone( bonenum );
for ( i = 0; i < 2 ; ++i )
{
char szName[ 512 ];
Q_snprintf( szName, sizeof( szName ), "%s_bone%s %d", basename, suffix[ i ], bonenum );
CDmeChannel *pAttachChannel = NULL;
if ( srcChannelsClip )
{
pAttachChannel = FindChannelTargetingElement( srcChannelsClip, pBoneTxForm, channelNames[ i ] );
}
if ( !pAttachChannel )
{
// Create one
pAttachChannel = CreateElement< CDmeChannel >( szName, channelsClip->GetFileId() );
Assert( pAttachChannel );
pAttachChannel->SetOutput( pBoneTxForm, channelNames[ i ], 0 );
}
if ( !pAttachChannel )
continue;
if ( bAttachToGameRecording && srcChannelsClip )
{
// Remove channel from channels clip
int idx = srcChannelsClip->m_Channels.Find( pAttachChannel->GetHandle() );
if ( idx != srcChannelsClip->m_Channels.InvalidIndex() )
{
srcChannelsClip->m_Channels.Remove( idx );
}
channelsClip->m_Channels.AddToTail( pAttachChannel );
}
control->SetValue( channelNames[ i ], pAttachChannel );
control->AddAttribute( valueNames[ i ], channelTypes[ i ] );
CDmeLog *pOriginalLog = pAttachChannel->GetLog();
pAttachChannel->SetMode( CM_PLAY );
pAttachChannel->SetInput( control, valueNames[ i ] );
// Transfer the logs over to the
if ( bAttachToGameRecording && pOriginalLog && srcChannelsClip )
{
CDmeLog *pNewLog = pAttachChannel->GetLog();
if ( pNewLog != pOriginalLog )
{
pAttachChannel->SetLog( pOriginalLog );
g_pDataModel->DestroyElement( pNewLog->GetHandle() );
}
DmeTime_t tLogToGlobal[ 2 ];
Assert(0);
// NOTE: Fix the next 2 lines to look like createsfmanimation.cpp
DmeTime_t curtime = DMETIME_ZERO; //doc->GetTime();
DmeTime_t cmt = DMETIME_ZERO; //doc->ToCurrentMediaTime( curtime, false );
DmeTime_t channelscliptime = shot->ToChildMediaTime( cmt, false );
DmeTime_t logtime = channelsClip->ToChildMediaTime( channelscliptime, false );
tLogToGlobal[ 0 ] = curtime - logtime;
DmeTime_t attachlogtime = srcChannelsClip->ToChildMediaTime( channelscliptime, false );
tLogToGlobal[ 1 ] = curtime - attachlogtime;
DmeTime_t offset = tLogToGlobal[ 1 ] - tLogToGlobal[ 0 ];
if ( DMETIME_ZERO != offset )
{
int c = pOriginalLog->GetKeyCount();
for ( int iLog = 0; iLog < c; ++iLog )
{
DmeTime_t keyTime = pOriginalLog->GetKeyTime( iLog );
keyTime += offset;
pOriginalLog->SetKeyTime( iLog, keyTime );
}
}
continue;
}
if ( pOriginalLog )
{
pOriginalLog->ClearKeys();
}
CDmeLog *log = pAttachChannel->GetLog();
if ( !log )
{
log = pAttachChannel->CreateLog( channelTypes[ i ] );
}
log->SetValueThreshold( 0.0f );
if ( bAttachToGameRecording )
{
Vector pos;
Quaternion rot;
matrix3x4_t matrix;
pBoneTxForm->GetTransform( matrix );
MatrixAngles( matrix, rot, pos );
if ( i == 0 )
{
((CDmeTypedLog< Vector > *)log)->SetKey( DMETIME_ZERO, pos );
}
else
{
((CDmeTypedLog< Quaternion > *)log)->SetKey( DMETIME_ZERO, rot );
}
continue;
}
CStudioHdr studiohdr( hdr, g_pMDLCache );
Vector pos[ MAXSTUDIOBONES ];
Quaternion q[ MAXSTUDIOBONES ];
float poseparameter[ MAXSTUDIOPOSEPARAM ];
for ( int pp = 0; pp < MAXSTUDIOPOSEPARAM; ++pp )
{
poseparameter[ pp ] = 0.0f;
}
// Set up skeleton
IBoneSetup boneSetup( &studiohdr, BONE_USED_BY_ANYTHING, poseparameter );
boneSetup.InitPose( pos, q );
boneSetup.AccumulatePose( pos, q, 0, 0.0f, 1.0f, 0.0f, NULL );
if ( i == 0 )
{
((CDmeTypedLog< Vector > *)log)->SetKey( DMETIME_ZERO, pos[ bonenum ] );
pBoneTxForm->SetPosition( pos[ bonenum ]);
}
else
{
((CDmeTypedLog< Quaternion > *)log)->SetKey( DMETIME_ZERO, q[ bonenum ] );
pBoneTxForm->SetOrientation( q[ bonenum ] );
}
}
}
//-----------------------------------------------------------------------------
// Sets up the root transform
//-----------------------------------------------------------------------------
static void SetupRootTransform( CDmeFilmClip *shot, CDmeChannelsClip *srcChannelsClip,
CDmeChannelsClip *channelsClip, CDmElement *control, CDmeGameModel *gameModel, const char *basename, bool bAttachToGameRecording )
{
char *channelNames[] = { "position", "orientation" };
char *valueNames[] = { "valuePosition", "valueOrientation" };
DmAttributeType_t channelTypes[] = { AT_VECTOR3, AT_QUATERNION };
const char *suffix[] = { "Pos", "Rot" };
DmAttributeType_t logType[] = { AT_VECTOR3, AT_QUATERNION };
int i;
for ( i = 0; i < 2 ; ++i )
{
char szName[ 512 ];
Q_snprintf( szName, sizeof( szName ), "%s_root%s channel", basename, suffix[ i ] );
CDmeChannel *pAttachChannel = NULL;
if ( srcChannelsClip )
{
pAttachChannel = FindChannelTargetingElement( srcChannelsClip, gameModel->GetTransform(), channelNames[ i ] );
}
if ( !pAttachChannel )
{
// Create one
pAttachChannel = CreateElement< CDmeChannel >( szName, channelsClip->GetFileId() );
Assert( pAttachChannel );
pAttachChannel->SetOutput( gameModel->GetTransform(), channelNames[ i ], 0 );
}
if ( bAttachToGameRecording && srcChannelsClip )
{
// Remove channel from channels clip
int idx = srcChannelsClip->m_Channels.Find( pAttachChannel->GetHandle() );
if ( idx != srcChannelsClip->m_Channels.InvalidIndex() )
{
srcChannelsClip->m_Channels.Remove( idx );
}
channelsClip->m_Channels.AddToTail( pAttachChannel );
}
control->SetValue( channelNames[ i ], pAttachChannel );
control->AddAttribute( valueNames[ i ], channelTypes[ i ] );
CDmeLog *pOriginalLog = pAttachChannel->GetLog();
pAttachChannel->SetMode( CM_PLAY );
pAttachChannel->SetInput( control, valueNames[ i ] );
if ( bAttachToGameRecording && pOriginalLog && srcChannelsClip )
{
CDmeLog *pNewLog = pAttachChannel->GetLog();
if ( pNewLog != pOriginalLog )
{
pAttachChannel->SetLog( pOriginalLog );
g_pDataModel->DestroyElement( pNewLog->GetHandle() );
}
RetimeLogData( srcChannelsClip, channelsClip, pOriginalLog );
}
else
{
Assert( !pOriginalLog );
CDmeLog *log = pAttachChannel->GetLog();
if ( !log )
{
log = pAttachChannel->CreateLog( logType[ i ] );
}
log->SetValueThreshold( 0.0f );
Vector vecPos;
Quaternion qOrientation;
matrix3x4_t txform;
gameModel->GetTransform()->GetTransform( txform );
MatrixAngles( txform, qOrientation, vecPos );
if ( i == 0 )
{
((CDmeTypedLog< Vector > *)log)->SetKey( DMETIME_ZERO, vecPos );
}
else
{
((CDmeTypedLog< Quaternion > *)log)->SetKey( DMETIME_ZERO, qOrientation );
}
}
}
}
//-----------------------------------------------------------------------------
// Creates preset groups for new animation sets
//-----------------------------------------------------------------------------
static bool ShouldRandomize( const char *name )
{
if ( !Q_stricmp( name, "eyes_updown" ) )
return false;
if ( !Q_stricmp( name, "eyes_rightleft" ) )
return false;
if ( !Q_stricmp( name, "lip_bite" ) )
return false;
if ( !Q_stricmp( name, "blink" ) )
return false;
if ( Q_stristr( name, "sneer" ) )
return false;
return true;
}
static void CreateProceduralPreset( CDmePresetGroup *pPresetGroup, const char *pPresetName, const CDmaElementArray< CDmElement > &controls, bool bIdentity, float flForceValue = 0.5f )
{
CDmePreset *pPreset = pPresetGroup->FindOrAddPreset( pPresetName );
int c = controls.Count();
for ( int i = 0; i < c ; ++i )
{
CDmElement *pControl = controls[ i ];
// Setting values on transforms doesn't make sense right now
if ( pControl->GetValue<bool>( "transform" ) )
continue;
bool bIsCombo = pControl->GetValue< bool >( "combo" );
bool bIsMulti = pControl->GetValue< bool >( "multi" );
bool bRandomize = ShouldRandomize( pControl->GetName() );
if ( !bIdentity && !bRandomize )
continue;
CDmElement *pControlValue = pPreset->FindOrAddControlValue( pControl->GetName() );
if ( !bIdentity )
{
pControlValue->SetValue< float >( "value", RandomFloat( 0.0f, 1.0f ) );
if ( bIsCombo )
{
pControlValue->SetValue< float >( "balance", RandomFloat( 0.25f, 0.75f ) );
}
if ( bIsMulti )
{
pControlValue->SetValue< float >( "multilevel", RandomFloat( 0.0f, 1.0f ) );
}
}
else
{
pControlValue->SetValue< float >( "value", flForceValue );
if ( bIsCombo )
{
pControlValue->SetValue< float >( "balance", 0.5f );
}
if ( bIsMulti )
{
pControlValue->SetValue< float >( "multilevel", flForceValue );
}
}
}
}
//-----------------------------------------------------------------------------
// Creates preset groups for new animation sets
//-----------------------------------------------------------------------------
static void CreatePresetGroups( CDmeAnimationSet *pAnimationSet, const char *pModelName )
{
CDmaElementArray< CDmElement > &controls = pAnimationSet->GetControls();
// Now create some presets
CDmePresetGroup *pProceduralPresets = pAnimationSet->FindOrAddPresetGroup( "procedural" );
pProceduralPresets->m_bIsReadOnly = true;
pProceduralPresets->FindOrAddPreset( "Default" );
CreateProceduralPreset( pProceduralPresets, "Zero", controls, true, 0.0f );
CreateProceduralPreset( pProceduralPresets, "Half", controls, true, 0.5f );
CreateProceduralPreset( pProceduralPresets, "One", controls, true, 1.0f );
// Add just one fake one for now
CreateProceduralPreset( pProceduralPresets, "Random", controls, false );
// These are the truly procedural ones...
pAnimationSet->EnsureProceduralPresets();
// Also load the model-specific presets
g_pModelPresetGroupMgr->ApplyModelPresets( pModelName, pAnimationSet );
}
//-----------------------------------------------------------------------------
// Destroys existing group mappings
//-----------------------------------------------------------------------------
static void RemoveExistingGroupMappings( CDmeAnimationSet *pAnimationSet )
{
CDmaElementArray<> &groups = pAnimationSet->GetSelectionGroups();
int nCount = groups.Count();
for ( int i = 0; i < nCount; ++i )
{
CDmElement *pGroup = groups[i];
groups.Set( i, NULL );
DestroyElement( pGroup );
}
groups.RemoveAll();
}
void LoadDefaultGroupMappings( CUtlDict< CUtlString, int > &defaultGroupMapping, CUtlVector< CUtlString >& defaultGroupOrdering )
{
defaultGroupMapping.RemoveAll();
defaultGroupOrdering.RemoveAll();
KeyValues *pGroupFile = new KeyValues( "groupFile" );
if ( !pGroupFile )
return;
if ( !pGroupFile->LoadFromFile( g_pFullFileSystem, ANIMATION_SET_DEFAULT_GROUP_MAPPING_FILE, "GAME" ) )
{
pGroupFile->deleteThis();
return;
}
// Fill in defaults
for ( KeyValues *sub = pGroupFile->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
{
const char *pGroupName = sub->GetName();
if ( !pGroupName )
{
Warning( "%s is malformed\n", ANIMATION_SET_DEFAULT_GROUP_MAPPING_FILE );
continue;
}
int i = defaultGroupOrdering.AddToTail();
defaultGroupOrdering[i] = pGroupName;
for ( KeyValues *pControl = sub->GetFirstSubKey(); pControl; pControl = pControl->GetNextKey() )
{
Assert( !Q_stricmp( pControl->GetName(), "control" ) );
CUtlString controlName = pControl->GetString();
defaultGroupMapping.Insert( controlName, pGroupName );
}
}
pGroupFile->deleteThis();
}
CDmElement *FindOrAddDefaultGroupForControls( const char *pGroupName, CDmaElementArray< CDmElement > &groups, DmFileId_t fileid )
{
// Now see if this group exists in the array
int c = groups.Count();
for ( int i = 0; i < c; ++i )
{
CDmElement *pGroup = groups[ i ];
if ( !Q_stricmp( pGroup->GetName(), pGroupName ) )
return pGroup;
}
CDmElement *pGroup = CreateElement< CDmElement >( pGroupName, fileid );
pGroup->AddAttribute( "selectedControls", AT_STRING_ARRAY );
groups.AddToTail( pGroup );
return pGroup;
}
//-----------------------------------------------------------------------------
// Build group mappings
//-----------------------------------------------------------------------------
static void BuildGroupMappings( CDmeAnimationSet *pAnimationSet )
{
RemoveExistingGroupMappings( pAnimationSet );
// Maps flex controls to first level "groups" by flex controller name
CUtlDict< CUtlString, int > defaultGroupMapping;
CUtlVector< CUtlString > defaultGroupOrdering;
LoadDefaultGroupMappings( defaultGroupMapping, defaultGroupOrdering );
// Create the default groups in order
CDmaElementArray<> &groups = pAnimationSet->GetSelectionGroups();
int nCount = defaultGroupOrdering.Count();
for ( int i = 0; i < nCount; ++i )
{
const char *pGroupName = (const char *)defaultGroupOrdering[ i ];
if ( !Q_stricmp( pGroupName, "IGNORE" ) )
continue;
CDmElement *pGroup = CreateElement< CDmElement >( pGroupName, pAnimationSet->GetFileId() );
// Fill in members
pGroup->AddAttribute( "selectedControls", AT_STRING_ARRAY );
groups.AddToTail( pGroup );
}
// Populate the groups with the controls
CDmaElementArray<> &controls = pAnimationSet->GetControls();
nCount = controls.Count();
for ( int i = 0; i < nCount; ++i )
{
const char *pGroupName = "Unknown";
const char *pControlName = controls[ i ]->GetName();
// Find the default if there is one
int idx = defaultGroupMapping.Find( pControlName );
if ( idx != defaultGroupMapping.InvalidIndex() )
{
pGroupName = defaultGroupMapping[ idx ];
}
else if ( Q_stristr( pControlName, "root" ) || Q_stristr( pControlName, "Valve" ) )
{
pGroupName = "Root";
}
if ( !Q_stricmp( pGroupName, "IGNORE" ) )
continue;
CDmElement *pGroup = FindOrAddDefaultGroupForControls( pGroupName, groups, pAnimationSet->GetFileId() );
// Fill in members
CDmrStringArray selectedControls( pGroup, "selectedControls" );
Assert( selectedControls.IsValid() );
if ( selectedControls.IsValid() )
{
selectedControls.AddToTail( pControlName );
}
}
}
void AddIllumPositionAttribute( CDmeGameModel *pGameModel )
{
studiohdr_t *pHdr = pGameModel->GetStudioHdr();
if ( !pHdr )
return;
if ( pHdr->IllumPositionAttachmentIndex() > 0 )
return; // don't add attr if model already has illumposition attachment
CDmAttribute *pAttr = pGameModel->AddAttributeElement< CDmeDag >( "illumPositionDag" );
Assert( pAttr );
if ( !pAttr )
return;
Assert( pGameModel->GetChildCount() > 0 );
pAttr->SetValue( pGameModel->GetChild( 0 ) );
}
//-----------------------------------------------------------------------------
// Creates an animation set
//-----------------------------------------------------------------------------
CDmeAnimationSet *CreateAnimationSet( CDmeFilmClip *pMovie, CDmeFilmClip *pShot,
CDmeGameModel *pGameModel, const char *pAnimationSetName, int nSequenceToUse, bool bAttachToGameRecording )
{
CDmeAnimationSet *pAnimationSet = CreateElement< CDmeAnimationSet >( pAnimationSetName, pMovie->GetFileId() );
Assert( pAnimationSet );
studiohdr_t *hdr = pGameModel->GetStudioHdr();
// Associate this animation set with a specific game model
// FIXME: Should the game model refer back to this set?
pAnimationSet->SetValue( "gameModel", pGameModel );
CDmeChannelsClip* pChannelsClip = CreateChannelsClip( pAnimationSet, pShot );
// Does everything associated with building facial controls on a model
CFlexControlBuilder builder;
builder.CreateAnimationSetControls( pMovie, pAnimationSet, pGameModel, pShot, pChannelsClip, bAttachToGameRecording );
// Create animation data if there wasn't any already in the model
if ( !bAttachToGameRecording )
{
CreateConstantValuedLog( pChannelsClip, pAnimationSetName, "skin", pGameModel, "skin", (int)0 );
CreateConstantValuedLog( pChannelsClip, pAnimationSetName, "body", pGameModel, "body", (int)0 );
CreateConstantValuedLog( pChannelsClip, pAnimationSetName, "sequence", pGameModel, "sequence", (int)0 );
CreateAnimationLogs( pChannelsClip, pGameModel, hdr, pAnimationSetName, nSequenceToUse, 0.0f, 1.0f, 0.05f );
}
CDmeChannelsClip *srcChannelsClip = FindChannelsClipTargetingDmeGameModel( pShot, pGameModel );
CDmaElementArray<> &controls = pAnimationSet->GetControls();
// First the root transform
{
const char *ctrlName = "rootTransform";
// Add the control to the controls group
CDmElement *ctrl = CreateElement< CDmElement >( ctrlName, pMovie->GetFileId() );
Assert( ctrl );
ctrl->SetValue< bool >( "transform", true );
controls.AddToTail( ctrl );
SetupRootTransform( pShot, srcChannelsClip, pChannelsClip, ctrl, pGameModel, pAnimationSetName, bAttachToGameRecording );
}
// Now add the bone transforms as well
{
int numbones = hdr->numbones;
for ( int b = 0; b < numbones; ++b )
{
mstudiobone_t *bone = hdr->pBone( b );
const char *name = bone->pszName();
// Add the control to the controls group
CDmElement *ctrl = CreateElement< CDmElement >( name, pMovie->GetFileId() );
Assert( ctrl );
ctrl->SetValue< bool >( "transform", true );
controls.AddToTail( ctrl );
SetupBoneTransform( pShot, srcChannelsClip, pChannelsClip, ctrl, pGameModel, pAnimationSetName, hdr, b, name, bAttachToGameRecording );
}
}
// Now copy all remaining logs, and retime them, over to the animation set channels clip...
if ( srcChannelsClip )
{
TransferRemainingChannels( pShot, pChannelsClip, srcChannelsClip );
}
// Create default preset groups for the animation set
CreatePresetGroups( pAnimationSet, pGameModel->GetModelName() );
// Builds the preset groups displayed in the upper left of the animation set panel
BuildGroupMappings( pAnimationSet );
pShot->AddAnimationSet( pAnimationSet );
AddIllumPositionAttribute( pGameModel );
return pAnimationSet;
}

42
sfmobjects/sfmobjects.vpc Normal file
View File

@ -0,0 +1,42 @@
//-----------------------------------------------------------------------------
// SFMOBJECTS.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;SFMOBJECTS_LIB"
}
}
$Project "sfmobjects"
{
$Folder "Header Files"
{
}
$Folder "Source Files"
{
$File "exportfacialanimation.cpp"
$File "flexcontrolbuilder.cpp"
$File "sfmsession.cpp"
$File "sfmanimationsetutils.cpp"
$File "sfmphonemeextractor.cpp"
}
$Folder "Interface"
{
$File "$SRCDIR\public\sfmobjects\exportfacialanimation.h"
$File "$SRCDIR\public\sfmobjects\flexcontrolbuilder.h"
$File "$SRCDIR\public\sfmobjects\sfmanimationsetutils.h"
$File "$SRCDIR\public\sfmobjects\sfmphonemeextractor.h"
$File "$SRCDIR\public\sfmobjects\sfmsession.h"
}
}

File diff suppressed because it is too large Load Diff

285
sfmobjects/sfmsession.cpp Normal file
View File

@ -0,0 +1,285 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// A class representing session state for the SFM
//
//=============================================================================
#include "sfmobjects/sfmsession.h"
#include "studio.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmetrack.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmecamera.h"
#include "movieobjects/dmetimeselection.h"
#include "movieobjects/dmeanimationset.h"
#include "movieobjects/dmegamemodel.h"
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CSFMSession::CSFMSession()
{
m_hRoot = NULL;
}
//-----------------------------------------------------------------------------
// Sets the root
//-----------------------------------------------------------------------------
void CSFMSession::SetRoot( CDmElement *pRoot )
{
m_hRoot = pRoot;
}
//-----------------------------------------------------------------------------
// Creates a new (empty) session
//-----------------------------------------------------------------------------
void CSFMSession::Init()
{
m_hRoot = NULL;
// a movie currently consists of: (this is all just temporary until clips take over more completely)
// a generic "root" node
// movie - a clip node whose subclips are the movie sequence
// cameras - an array of cameras used throughout the movie
// clips - an array of clips used throughout the movie
CDmElement *pRoot = CreateElement< CDmElement >( "session" );
Assert( pRoot );
if ( !pRoot )
return;
m_hRoot = pRoot;
// FIXME!
pRoot->SetValue( "editorType", "ifm" );
CDmeFilmClip *pFilmClip = CreateElement<CDmeFilmClip>( "sequence" );
Assert( pFilmClip != NULL );
pFilmClip->SetDuration( DmeTime_t( 60.0f ) );
CDmeTrack *pTrack = pFilmClip->FindOrCreateFilmTrack();
CDmeClip *pShot = CreateElement<CDmeFilmClip>( "shot" );
pTrack->AddClip( pShot );
pShot->SetDuration( DmeTime_t( 60.0f ) );
pRoot->SetValue( "activeClip", pFilmClip );
pRoot->AddAttributeElementArray< CDmElement >( "miscBin" );
pRoot->AddAttributeElementArray< CDmeCamera >( "cameraBin" );
CDmAttribute *pClipBin = pRoot->AddAttributeElementArray< CDmeClip >( "clipBin" );
// Don't allow duplicates in the clipBin
pClipBin->AddFlag( FATTRIB_NODUPLICATES );
CDmrElementArray<> clipBin( pRoot, "clipBin" );
clipBin.AddToTail( pFilmClip );
CreateSessionSettings();
}
//-----------------------------------------------------------------------------
// Shuts down the session
//-----------------------------------------------------------------------------
void CSFMSession::Shutdown()
{
if ( m_hRoot.Get() )
{
if ( m_hRoot->GetFileId() != DMFILEID_INVALID )
{
g_pDataModel->RemoveFileId( m_hRoot->GetFileId() );
m_hRoot = NULL;
}
}
}
//-----------------------------------------------------------------------------
// Creates session settings
//-----------------------------------------------------------------------------
void CSFMSession::CreateSessionSettings()
{
if ( !m_hRoot.Get() )
return;
m_hRoot->AddAttribute( "settings", AT_ELEMENT );
CDmElement *pSettings = m_hRoot->GetValueElement< CDmElement >( "settings" );
if ( !pSettings )
{
pSettings = CreateElement< CDmElement >( "sessionSettings", m_hRoot->GetFileId() );
m_hRoot->SetValue( "settings", pSettings );
}
Assert( pSettings );
CDmeTimeSelection *ts = NULL;
if ( !pSettings->HasAttribute( "timeSelection" ) )
{
ts = CreateElement< CDmeTimeSelection >( "timeSelection", m_hRoot->GetFileId() );
pSettings->SetValue( "timeSelection", ts );
}
pSettings->InitValue( "animationSetOverlayBackground", Color( 0, 0, 0, 192 ) );
if ( !pSettings->HasAttribute( "standardColors" ) )
{
CDmrArray<Color> colors( pSettings, "standardColors", true );
colors.AddToTail( Color( 0, 0, 0, 128 ) );
colors.AddToTail( Color( 194, 120, 0, 128 ) );
colors.AddToTail( Color( 255, 0, 100, 128 ) );
colors.AddToTail( Color( 200, 200, 255, 128 ) );
colors.AddToTail( Color( 255, 255, 255, 128 ) );
}
float flLegacyFrameRate = 24.0f;
if ( pSettings->HasAttribute( "frameRate" ) )
{
flLegacyFrameRate = pSettings->GetValue<float>( "frameRate" );
// remove this from the base level settings area since we're going to add it in renderSettings
pSettings->RemoveAttribute( "frameRate" );
}
if ( !pSettings->HasAttribute( "proceduralPresets" ) )
{
CDmeProceduralPresetSettings *ps = CreateElement< CDmeProceduralPresetSettings >( "proceduralPresets", m_hRoot->GetFileId() );
pSettings->SetValue( "proceduralPresets", ps );
}
CreateRenderSettings( pSettings, flLegacyFrameRate );
}
//-----------------------------------------------------------------------------
// Creates session render settings
// JasonM - remove flLegacyFramerate param eventually (perhaps March 2007)
//-----------------------------------------------------------------------------
void CSFMSession::CreateRenderSettings( CDmElement *pSettings, float flLegacyFramerate )
{
pSettings->AddAttribute( "renderSettings", AT_ELEMENT );
CDmElement *pRenderSettings = pSettings->GetValueElement< CDmElement >( "renderSettings" );
if ( !pRenderSettings )
{
pRenderSettings = CreateElement< CDmElement >( "renderSettings", pSettings->GetFileId() );
pSettings->SetValue( "renderSettings", pRenderSettings );
}
Assert( pRenderSettings );
pRenderSettings->InitValue( "frameRate", flLegacyFramerate ); // Default framerate
pRenderSettings->InitValue( "lightAverage", 0 ); // Don't light average by default
pRenderSettings->InitValue( "showFocalPlane", 0 ); // Don't show focal plane by default
pRenderSettings->InitValue( "modelLod", 0 ); // Don't do model LOD by default
CreateProgressiveRefinementSettings( pRenderSettings );
}
//-----------------------------------------------------------------------------
// Creates session progrssing refinement settings
//-----------------------------------------------------------------------------
void CSFMSession::CreateProgressiveRefinementSettings( CDmElement *pRenderSettings )
{
// Do we already have Progressive refinement settings?
CDmElement *pRefinementSettings = pRenderSettings->GetValueElement< CDmElement >( "ProgressiveRefinement" );
if ( !pRefinementSettings )
{
pRefinementSettings = CreateElement< CDmElement >( "ProgressiveRefinementSettings", pRenderSettings->GetFileId() );
pRenderSettings->SetValue( "ProgressiveRefinement", pRefinementSettings );
}
// Set up defaults for progressive refinement settings...
pRefinementSettings->InitValue( "on", true );
pRefinementSettings->InitValue( "useDepthOfField", true );
pRefinementSettings->InitValue( "overrideDepthOfFieldQuality", false );
pRefinementSettings->InitValue( "overrideDepthOfFieldQualityValue", 1 );
pRefinementSettings->InitValue( "useMotionBlur", true );
pRefinementSettings->InitValue( "overrideMotionBlurQuality", false );
pRefinementSettings->InitValue( "overrideMotionBlurQualityValue", 1 );
pRefinementSettings->InitValue( "useAntialiasing", false );
pRefinementSettings->InitValue( "overrideShutterSpeed", false );
pRefinementSettings->InitValue( "overrideShutterSpeedValue", 1.0f / 48.0f );
}
//-----------------------------------------------------------------------------
// Creates a camera
//-----------------------------------------------------------------------------
CDmeCamera *CSFMSession::CreateCamera( const DmeCameraParams_t& params )
{
CDmeCamera *pCamera = CreateElement< CDmeCamera >( params.name, m_hRoot->GetFileId() );
// Set parameters
matrix3x4_t txform;
AngleMatrix( params.angles, params.origin, txform );
CDmeTransform *pTransform = pCamera->GetTransform();
if ( pTransform )
{
pTransform->SetTransform( txform );
}
pCamera->SetFOVx( params.fov );
return pCamera;
}
//-----------------------------------------------------------------------------
// Finds or creates a scene
//-----------------------------------------------------------------------------
CDmeDag *CSFMSession::FindOrCreateScene( CDmeFilmClip *pShot, const char *pSceneName )
{
CDmeDag *pScene = pShot->GetScene();
if ( !pScene )
{
pScene = CreateElement< CDmeDag >( "scene", pShot->GetFileId() );
pShot->SetScene( pScene );
}
Assert( pScene );
int c = pScene->GetChildCount();
for ( int i = 0 ; i < c; ++i )
{
CDmeDag *pChild = pScene->GetChild( i );
if ( pChild && !Q_stricmp( pChild->GetName(), pSceneName ) )
return pChild;
}
CDmeDag *pNewScene = CreateElement< CDmeDag >( pSceneName, pScene->GetFileId() );
pScene->AddChild( pNewScene );
return pNewScene;
}
//-----------------------------------------------------------------------------
// Creates a game model
//-----------------------------------------------------------------------------
CDmeGameModel *CSFMSession::CreateEditorGameModel( studiohdr_t *hdr, const Vector &vecOrigin, Quaternion &qOrientation )
{
char pBaseName[ 256 ];
Q_FileBase( hdr->pszName(), pBaseName, sizeof( pBaseName ) );
char pGameModelName[ 256 ];
Q_snprintf( pGameModelName, sizeof( pGameModelName ), "%s_GameModel", pBaseName );
CDmeGameModel *pGameModel = CreateElement< CDmeGameModel >( pGameModelName, m_hRoot->GetFileId() );
char pRelativeModelsFileName[MAX_PATH];
Q_ComposeFileName( "models", hdr->pszName(), pRelativeModelsFileName, sizeof(pRelativeModelsFileName) );
pGameModel->SetValue( "modelName", pRelativeModelsFileName );
CDmeTransform *pTransform = pGameModel->GetTransform();
if ( pTransform )
{
pTransform->SetPosition( vecOrigin );
pTransform->SetOrientation( qOrientation );
}
// create, connect and cache each bone's pos and rot channels
pGameModel->AddBones( hdr, pBaseName, 0, hdr->numbones );
return pGameModel;
}